何时使用std :: forward来转发参数?

coy*_*508 150 c++ templates forwarding c++11

C++ 0x显示了一个使用示例std::forward:

template<class T>
void foo(T&& arg) 
{
  bar(std::forward<T>(arg));
}
Run Code Online (Sandbox Code Playgroud)

什么时候使用它std::forward总是有利的?

另外,它需要&&在参数声明中使用,它在所有情况下都有效吗?如果函数在其中声明,我认为你必须将临时函数传递给函数&&,那么可以使用任何参数调用foo吗?

最后,如果我有一个函数调用,如下所示:

template<int val, typename... Params>
void doSomething(Params... args) {
  doSomethingElse<val, Params...>(args...);
}
Run Code Online (Sandbox Code Playgroud)

我应该用它代替:

template<int val, typename... Params>
void doSomething(Params&&... args) {
  doSomethingElse<val, Params...>(std::forward<Params>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

另外,如果在函数中使用两次参数,即同时转发到两个函数,是否明智使用std::forward?不会std::forward将相同的东西转换为临时两次,移动内存并使其无效以供第二次使用?以下代码是否可以:

template<int val, typename... Params>
void doSomething(Params&&... args) {
  doSomethingElse<val, Params...>(std::forward<Params>(args)...);
  doSomethingWeird<val, Params...>(std::forward<Params>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

我有点困惑std::forward,我很乐意使用一些清理工作.

Ker*_* SB 117

像第一个例子一样使用它:

template <typename T> void f(T && x)
{
  g(std::forward<T>(x));
}

template <typename ...Args> void f(Args && ...args)
{
  g(std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

这是因为参考折叠规则:如果T = U&,那么T&& = U&,如果T = U&&,那么T&& = U&&,所以你总是在函数体内得到正确的类型.最后,你需要forward将左值变为x(因为它现在有一个名字!),如果最初是左值,则将其转换为右值引用.

但是,你不应该多次转发一些东西,因为这通常没有意义:转发意味着你可能会将参数一直移动到最终的调用者,一旦它被移动它就会消失,所以你不能再使用它再次(以你可能想要的方式).

  • @DeadMG:它始终是正确的,而不是我错误记录的那个:-) ...虽然在这种情况下我似乎错误地记错了! (5认同)
  • @cmdLP:您说得对,重复转发是明确定义的,但是对于您的程序来说,它在语义上很少是正确的。不过,使用前向表达式的成员是一个有用的案例。我会更新答案。 (2认同)

Mil*_*kic 8

Kerrek 的回答很有用,但并没有完全回答标题中的问题:

何时使用 std::forward 转发参数?

为了回答这个问题,我们首先应该引入一个通用引用的概念。Scott Meyers 给了这个名字,现在他们通常被称为转发引用。基本上,当你看到这样的东西时:

template<typename T>
void f(T&& param);
Run Code Online (Sandbox Code Playgroud)

请记住,这param不是右值引用(因为人们可能会得出结论),而是通用引用*。通用引用的特点是非常受限制的形式(只是T&&,没有 const 或类似的限定符)和类型推导- 类型T将在f被调用时推导。简而言之,通用引用对应于用右值初始化的右值引用,如果它们用左值初始化则对应于左值引用。

现在回答原始问题相对容易 - 适用std::forward于:

  • 上次在函数中使用时的通用引用
  • 从按值返回的函数返回的通用引用

第一种情况的示例:

template<typename T>
void foo(T&& prop) {
    other.set(prop); // use prop, but don't modify it because we still need it
    bar(std::forward<T>(prop)); // final use -> std::forward
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我们不想propother.set(..)完成后有一些未知的值,所以这里没有转发。然而,当bar我们调用prop它时,我们已经完成了它并且bar可以用它做任何它想做的事情(例如移动它)。

第二种情况的示例:

template<typename T>
Widget transform(T&& prop) {
   prop.transform();
   return std::forward<T>(prop);
}
Run Code Online (Sandbox Code Playgroud)

prop如果它是一个右值,这个函数模板应该移动到返回值中,如果它是一个左值,则复制它。如果我们std::forward在最后省略,我们总是会创建一个副本,当prop碰巧是一个右值时,这会更昂贵。

*完全准确地说,通用引用是对 cv-unqualified 模板参数进行右值引用的概念。