-
在函数中,对右值引用的参数使用
std::move
,对万能引用的参数使用std::forward
,不能用反:- 对右值引用使用
std::forward<T>()
需要额外写一遍类型T
,代码冗余容易出错; - 对万能引用使用
std::move
更糟糕,因为万能引用参数可以绑定到左值引用,使用std::move
可能会错误地破坏传入的左值参数(准确来说,是错误地将其强制转型为右值,导致后续使用时可能被破坏)。
- 对右值引用使用
-
在一个函数中如果要多次使用一个右值/万能引用对象,仅在最后一次对其用
std::move
或std::forward
。 -
对于一个返回值类型为按值返回(by value)的函数,如果返回的是右值引用或万能引用对象,应该使用
std::move
或std::forward
,例如:Matrix // by-value return operator+(Matrix&& lhs, const Matrix& rhs) { lhs += rhs; return std::move(lhs); // move lhs into } // return value
若是写为 return lhs
,则会导致 lhs
被拷贝而非移动到返回值,通常开销更大。即使 Matrix
类型暂时不支持移动构造也无妨,此时移动会默认降级为拷贝操作。如果后续 Matrix
支持了移动构造,该函数也会自动从中收益,转换为移动操作。
- 注意,以上规则适用于函数的右值或万能引用参数,不能扩展到函数的局部变量!
Widget makeWidget()
{
Widget w; // local variable
... // configure w
return w; // "copy" w into return value
return std::move(w); // move w into return value (don't do this!)
}
上面的 return 语句看起来好像做了一次不必要的从局部变量 w
到返回值的拷贝操作,但实际并非如此。在C++11提出移动语义很久之前,标准委员会就已经注意到这个问题,并提出可以通过直接在函数返回值的内存位置构造 w
避免拷贝,这种技术称为 RVO/NRVO,即(Named) Return Value Optimization,(具名)返回值优化(二者的区别:前者针对 Widget w; return w;
,后者针对 return Widget()
)。该技术的更多细节可以参考《深度探索C++对象模型》第二章。
下面的 return 语句使用 std::move
,的确能享受到移动操作的好处;但其返回类型是 w
的引用,导致编译器无法对其使用 RVO 优化。而相比之下,RVO 的性能提升高于移动操作。况且,C++标准还规定:在可以使用 RVO 优化时,即使因某些原因无法实现(例如,函数内有多个 if-else 控制路径返回不同局部变量),编译器也应将返回值视为右值。换言之,上面的 return 语句要么能使用性能更好的优化技术,要么等同于下面的 return 语句。为局部变量添加 std::move
完全是一种画蛇添足的行为。
总结
- 在最后一次使用时,对右值引用参数使用
std::move
,对万能引用参数使用std::forward
。 - 如果函数返回类型是按值返回,且返回值是右值或万能引用,也应对其使用
std::move
或std::forward
。 - 永远不要在返回语句中对可以进行返回值优化(RVO)的局部变量使用
std::move
或std::forward
。