C++11 标准::forward_as_tuple 和标准::前进

C++11 std::forward_as_tuple and std::forward

本文关键字:标准 前进 tuple as forward C++11      更新时间:2023-10-16

当我使用函数参数作为std::forward_as_tuple参数时,我应该std::forward函数参数吗?

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,这是假设在调用 call_fn 之后不使用 list 中的参数。这是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)

类型 List&& ,其中 List 实际上是模板参数,是通用引用而不是 R 值引用。因此,您应该将它们std::forward std::forward_as_tuple函数,否则在std::forward_as_tuple内部,由于引用折叠,传递给fn的 r 值引用将显示为 l 值引用。