C ++ 11 std :: forward_as_tuple和std :: forward

时间:2014-08-21 13:43:13

标签: c++ templates c++11 tuples perfect-forwarding

当我将它们用作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)... ) );  
}

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

3 个答案:

答案 0 :(得分:6)

您必须使用std::forward才能将参数的值类别保留为fn()。由于参数的名称在fn之内,因此它们是左值,如果没有std::forward,它们将始终传递给std::forward_as_tuple

可以使用following example

来证明差异
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的情况下,参数将作为左值传递给{{1}}。

答案 1 :(得分:1)

是的,你几乎肯定想在这里使用std::forward,这是假设list中的参数在调用call_fn后没有使用。 这是std::forward的典型用例,因为您希望使用完美转发的语义

std::forward preserves the value category其论点(即左值为左值,左值为右值)。 std::forward_as_tuple反过来也会这样做,就像调用std::tuple<List&&...>(std::forward<List>(list)...)一样。

关于“存储为右值参考”的注释。并不是参数包中的参数List都是rvalues引用(它们可能是),但是在此上下文中推导出List,因此将应用引用折叠和推导类型(s )可以是右值引用或左值引用。在创建std::tuple期间,您希望维护/保留这种区别。

答案 2 :(得分:0)

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

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

类型List&&,其中List实际上是模板参数,是Universal Reference而不是r值引用。因此,您应std::forward将它们转换为std::forward_as_tuple函数,否则在std::forward_as_tuple内传递给fn的r值引用将作为l值引用显示,因为引用塌陷。