当我std::forward
将函数参数用作参数时,我应该使用它们std::forward_as_tuple
吗?
template<class ... List>
void fn(List&& ... list){
// do I need this forward?
call_fn( forward_as_tuple( forward<List>(list)... ) );
}
我知道它们将被存储为右值引用,但还有什么我应该考虑的吗?
当我std::forward
将函数参数用作参数时,我应该使用它们std::forward_as_tuple
吗?
template<class ... List>
void fn(List&& ... list){
// do I need this forward?
call_fn( forward_as_tuple( forward<List>(list)... ) );
}
我知道它们将被存储为右值引用,但还有什么我应该考虑的吗?
您必须使用std::forward
才能将参数的值类别保留到fn()
. 由于参数在 中具有名称fn
,因此它们是左值,没有std::forward
它们将始终按原样传递给std::forward_as_tuple
。
可以使用以下示例演示差异:
template<typename T>
void bar2(T&& t)
{
std::cout << __PRETTY_FUNCTION__ << ' '
<< std::is_rvalue_reference<decltype(t)>::value << '\n';
}
template<typename T>
void bar1(T&& t)
{
std::cout << __PRETTY_FUNCTION__ << ' '
<< std::is_rvalue_reference<decltype(t)>::value << '\n';
bar2(std::forward<T>(t));
bar2(t);
}
bar1
总是将它的参数传递给bar2
,一次有std::forward
,一次没有。现在让我们用一个左值和一个右值参数来调用它们。
foo f;
bar1(f);
std::cout << "--------\n";
bar1(foo{});
输出:
void bar1(T&&) [with T = foo&] 0
void bar2(T&&) [with T = foo&] 0
void bar2(T&&) [with T = foo&] 0
--------
void bar1(T&&) [with T = foo] 1
void bar2(T&&) [with T = foo] 1
void bar2(T&&) [with T = foo&] 0
从输出中可以看出,在这两种情况下,在不使用 的情况下,std::forward
参数都作为左值传递给bar2
。
是的,你几乎肯定想在std::forward
这里使用,这是假设list
在调用 to 之后不使用in 的参数call_fn
。这是一个典型的用例std::forward
,因为您想练习完美转发的语义。
std::forward
保留其参数的值类别(即左值作为左值,右值作为右值)。std::forward_as_tuple
反过来也会做同样的事情,就像std::tuple<List&&...>(std::forward<List>(list)...)
被调用一样。
关于“存储为右值引用”的注释。不是List
参数包中的参数都是右值引用(它们可能是),而是List
在这种情况下推导出来,因此引用折叠将适用并且推导的类型可能是右值引用或左值引用。在创建 时std::tuple
,您想要维护/保留的正是这种区别。
是的,如果您想保留完美的转发语义。在您的示例中:
template<class ... List>
void fn(List&& ... list)
type实际上是一个模板参数List&&
,List
是一个通用引用而不是一个右值引用。因此,您应该std::forward
让它们发挥std::forward_as_tuple
作用,否则由于引用折叠,std::forward_as_tuple
传递给的 r 值引用内部将作为左值引用可见。fn