我可以通常/总是使用std :: forward而不是std :: move吗?

我一直在观看斯科特·迈耶斯(Scott Meyers) 关于 C ++和Beyond 2012会议上Universal Reference的演讲,到目前为止一切都很有意义。 然而,观众在大约50分钟的时间里问了一个问题,我也在想。 迈尔斯说,他不关心答案,因为它是非惯用的,会愚蠢的他的头脑,但我仍然感兴趣。

代码如下:

// Typical function bodies with overloading: void doWork(const Widget& param) // copy { // ops and exprs using param } void doWork(Widget&& param) // move { // ops and exprs using std::move(param) } // Typical function implementations with universal reference: template <typename T> void doWork(T&& param) // forward => copy and move { // ops and exprs using std::forward<T>(param) } 

重点是,当我们取右值引用时,我们知道我们有一个右值,所以我们应该std::move它以保留它是一个右值的事实。 当我们采用通用引用( T&& ,其中T是推导types)时,我们希望std::forward保留它可能是左值或右值的事实。

所以问题是:因为std::forward保留传递给函数的值是左值还是右值,而std::move只是将它的参数转换为右值,我们可以在任何地方使用std::forward吗? 在所有使用std::move情况下, std::forwardstd::move一样行为吗?还是在Meyers的泛化中错过了一些重要的行为差异?

我并不是说任何人都应该这么做,因为正如迈尔斯所说的那样,这完全是非惯用的,但下面也是对std::move的有效使用:

 void doWork(Widget&& param) // move { // ops and exprs using std::forward<Widget>(param) } 

这两者是非常不同和互补的工具。

  • std::move 推导参数并无条件地创build一个右值expression式。 这适用于实际的对象或variables是有意义的。

  • std::forward需要一个强制的模板参数(你必须指定这个参数!),并根据types(通过添加&&和折叠规则)神奇地创build一个左值引用或右值expression式。 这只适用于推导的模板函数参数。

也许下面的例子更好地说明了这一点:

 #include <utility> #include <memory> #include <vector> #include "foo.hpp" std::vector<std::unique_ptr<Foo>> v; template <typename T, typename ...Args> std::unique_ptr<T> make_unique(Args &&... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); // #1 } int main() { { std::unique_ptr<Foo> p(new Foo('a', true, Bar(1,2,3))); v.push_back(std::move(p)); // #2 } { v.push_back(make_unique<Foo>('b', false, Bar(5,6,7))); // #3 } { Bar b(4,5,6); char c = 'x'; v.push_back(make_unique<Foo>(c, b.ready(), b)); // #4 } } 

在情况#2中,我们有一个现存的具体的对象p ,我们想要无条件地从它移动。 只有std::move是有道理的。 这里没有什么“前进”的。 我们有一个命名的variables,我们想从中移动。

另一方面,情况#1接受任何types参数的列表,并且每个参数需要被转发为与原始调用中相同的值类别。 例如,在#3中,参数是临时expression式,因此它们将作为rvalues被转发。 但是我们也可以在构造函数调用中混合使用命名对象,如情况#4,然后我们需要作为左值转发。

是的,如果param是一个Widget&& ,那么以下三个expression式是等价的(假设Widget不是引用types):

 std::move(param) std::forward<Widget>(param) static_cast<Widget&&>(param) 

通常(当Widget可能是一个引用)时, std::move(param)等价于以下两个expression式:

 std::forward<std::remove_reference<Widget>::type>(param) static_cast<std::remove_reference<Widget>::type&&>(param) 

注意多less更好的std::move是移动的东西。 std::forward的要点在于它和模板types的扣减规则很好地混合在一起:

 template<typename T> void foo(T&& t) { std::forward<T>(t); std::move(t); } int main() { int a{}; int const b{}; //Deduced T Signature Result of `forward<T>` Result of `move` foo(a); //int& foo(int&) lvalue int xvalue int foo(b); //int const& foo(int const&) lvalue int const xvalue int const foo(int{});//int foo(int&&) xvalue int xvalue int }