12

当我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)... ) );  
}

我知道它们将被存储为右值引用,但还有什么我应该考虑的吗?

4

3 回答 3

9

您必须使用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

于 2014-08-21T14:23:06.627 回答
3

是的,你几乎肯定想在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,您想要维护/保留的正是这种区别。

于 2014-08-21T14:20:14.313 回答
1

是的,如果您想保留完美的转发语义。在您的示例中:

template<class ... List>
void fn(List&& ... list)

type实际上是一个模板参数List&&List是一个通用引用而不是一个右值引用。因此,您应该std::forward让它们发挥std::forward_as_tuple作用,否则由于引用折叠,std::forward_as_tuple传递给的 r 值引用内部将作为左值引用可见。fn

于 2014-08-21T14:01:54.433 回答