为什么我应该使用std::forward?

17
在下面的代码中,为什么我在传递参数时应该使用std::forward
class Test {
  public:
  Test() {
      std::cout << "ctor" << std::endl;
  }
  Test(const Test&) {
      std::cout << "copy ctor" << std::endl;
  }
  Test(const Test&&) {
      std::cout << "move ctor" << std::endl;
  }
};

template<typename Arg>
void pass(Arg&& arg) {
    // use arg..
    return; 
}

template<typename Arg, typename ...Args>
void pass(Arg&& arg, Args&&... args)
{
    // use arg...
    return pass(args...); // why should I use std::forward<Arg>(args)... ?
}

int main(int argc, char** argv)
{
    pass(std::move<Test>(Test()));

    return 0;
}

使用或不使用 std::forward 代码没有任何复制/移动操作。


1
相关;https://dev59.com/m3A65IYBdhLWcg3w4C2j - Niall
3
什么时候需要使用std::forward转发参数? - Drax
上述两个帖子有什么让您感到困惑的地方吗?或者它们解决了您的问题吗? - Yakk - Adam Nevraumont
1
Test(const Test&&)不是“移动构造函数”。Test(Test&&)才是。 - Emilio Garavaglia
1个回答

21

有许多关于std::forward的文章介绍它的作用和原理(例如这里这里)。

简而言之,它保留了其参数的值类别。完美转发的目的是确保将提供给函数的参数与最初提供的参数具有相同的值类别(基本上是r-value和l-value),以便将其转发到另一个函数中(或在函数内部使用)。它通常用于模板函数中,其中可能已发生引用折叠(涉及通用/转发引用)。

考虑下面的代码示例。如果删除std::forward,则会打印出需要左值,添加std::forward则会打印出需要右值。根据是rvalue还是lvalue,func进行了重载。如果没有使用std::forward调用它,则会调用错误的重载。在这种情况下,std::forward是必需的,因为pass是用一个rvalue调用的。
#include <utility>
#include <iostream>
class Test {
  public:
  Test() {
      std::cout << "ctor" << std::endl;
  }
  Test(const Test&) {
      std::cout << "copy ctor" << std::endl;
  }
  Test(Test&&) {
      std::cout << "move ctor" << std::endl;
  }
};

void func(Test const&)
{
    std::cout << "requires lvalue" << std::endl;
}

void func(Test&&)
{
    std::cout << "requires rvalue" << std::endl;
}

template<typename Arg>
void pass(Arg&& arg) {
    // use arg here
    func(std::forward<Arg>(arg));
    return; 
}

template<typename Arg, typename ...Args>
void pass(Arg&& arg, Args&&... args)
{
    // use arg here
    return pass(std::forward<Args>(args)...);
}

int main(int, char**)
{
    pass(std::move<Test>(Test()));
    return 0;
}

为什么调用Test时使用const&版本是不正确的?我的意思是:这样做的代价是什么? - Dean
2
@user3834459 这取决于函数的作用。如果两个重载之间的功能需要(或针对)值类别进行优化,则是,否则可能不是。一般而言,鉴于这两个重载,人们会认为存在某些差异,并且最好调用正确的版本。 - Niall
好的,明白了。谢谢! - Dean

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接