我正在尝试创建一个函数,它基本上接受函数指针以及它的所有参数,并将函数和这些参数存储在std::function和std::元组中以供以后使用。当然,我看到了使用可变参数的函数的典型模式,将它们完美地转发到某个地方进行存储。我在如何存储可变模板参数中看到了这个解决方案?,这在我的情况下工作得很好,直到我意识到它似乎只接受在std::ref()
或std::move()
d中包装的函数参数,而且我根本无法存储变量值。
在寻找另一种允许我通过值和引用存储参数的解决方案时,我遇到了c++存储函数和参数列表供以后使用,我打算重新利用它们。但同时,我真的很想理解为什么第一个解决方案不允许我按值传递任何东西:
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的身份在过程中丢失-你将有一个对初始化器的值参数的引用-这将是一个挂起引用。因此才有了这句话;在元组中的第1个位置不能通过按值传递的int参数初始化。
无论如何,解决方案也是以这种方式衰减args_声明:
std::tuple< std::decay_t< Ts > ...> args_;
然后编译得很好,因为您已经将接收者(args_)与make_tuple()的结果匹配起来,而make_tuple()会在内部衰减其参数。
则args_的类型最终为:
std::tuple<std::reference_wrapper<int>,int>
和所有是共生的。