C++ 将可变参数模板参数转发给元组

C++ Forward variadic templated arguments to tuple

我正在尝试创建一个基本上接受函数指针及其所有参数的函数,并将该函数和这些参数存储在 std::function 和 std::tuple 中以备后用采用。我当然看到了使用可变参数的函数的典型模式,完美地将它们转发到某个地方进行存储。我在 How to store variadic template arguments? 中看到了这个解决方案,它对我的​​情况很有效,直到我意识到它似乎只接受包裹在 std::ref()std::move()d 中的函数参数,并且我根本无法存储变量值。

在更多地搜索另一种允许我按值和引用存储参数的解决方案时,我遇到了 c++ store function and parameter list for later use,我打算重新调整它的用途。但与此同时,我真的很想了解为什么第一个解决方案不让我按值传递任何东西:

template <typename... Ts>
class TupleCarrier {
 public:
    TupleCarrier(Ts&&... args): args_(std::make_tuple(std::forward<Ts>(args)...)) {}

    std::tuple<Ts...> args_;
};

template <typename... Ts>
void CreateCarrier(Ts&&... args) {
    TupleCarrier<Ts...> carrier(std::forward<Ts>(args)...);
}

int main() {
    int test = 4;
    CreateCarrier(std::ref(test), /*std::ref(*/test/*)*/); // fails to compile
    return 0;
}

是不是就这么简单,CreateCarrier是把参数当作右值引用,而std::forward要么是把它们当作右值引用,要么是左值引用,但是根本没有办法传递这里有一个普通的旧左值?我希望左值至少会被隐式地视为左值引用,如果您打算按值传递,这当然会有问题,但至少它会编译...

无论如何,我认为我对上述内容的细微之处的理解不足以自己得出正确的答案,所以这里的某种解释确实可以帮助我理解一些稍微更高级的 C++ :)

问题在于 std::make_tuple() 的定义方式:它 std::decay<> 是它的参数类型。

检查https://en.cppreference.com/w/cpp/utility/tuple/make_tuple“可能的实施”部分以查看详细信息。

所以,发生的事情是,_args 成员最终成为:

std::tuple<std::reference_wrapper<int>,int &>

因为那里没有添加衰减。 然而 make_tuple() 的结果是 std::tuple< int&, int > - 你的 int 的身份在这个过程中丢失了 - 你将引用初始化程序的值参数 - 这将是悬挂参考。因此,第一个元组位置中的 int& 不能通过按值传递 int 参数进行初始化。

无论如何,解决方案也是以这种方式衰减 args_ 声明:

std::tuple< std::decay_t< Ts > ...> args_;

然后事情编译得很好,因为你已经将接收器 (args_) 与 make_tuple() 的结果匹配,后者在内部衰减了它的参数。

然后 args_ 的类型最终为:

std::tuple<std::reference_wrapper<int>,int>

一切都是共酶。