除非我错了,似乎工作很好 – 有一个最好的做法理由喜欢一个在另一个?
例:
struct A { A(){} A(const A&){ std::cout << "A(const A&)\n"; } A(A&&){ std::cout << "A(A&&)\n"; } }; struct B { B(){} B(const B& right) : x(right.x){ std::cout << "B(const B&)\n"; } B(B&& right) : x(std::forward<A>(right.x)){ std::cout << "B(B&&)\n"; } A x; }; struct C { C(){} C(const C& right) : x(right.x){ std::cout << "C(const C&)\n"; } C(C&& right) : x(std::move(right.x)){ std::cout << "C(C&&)\n"; } A x; }; struct D { D(){} D(const D& right) : x(right.x){ std::cout << "D(const D&)\n"; } D(D&& right) : x(right.x){ std::cout << "D(D&&)\n"; } A x; }; int main() { std::cout << "--- B Test ---\n"; B b1; B b2(std::move(b1)); std::cout << "--- C Test ---\n"; C c1; C c2(std::move(c1)); std::cout << "--- D Test ---\n"; D d1; D d2(std::move(d1)); }
输出:
--- B Test --- A(A&&) B(B&&) --- C Test --- A(A&&) C(C&&) --- D Test --- A(const A&) D(D&&)
解决方法
问题是:这些真的是该类的移动构造函数/赋值运算符吗?或者他们只是从你眼睛的角度看起来像?
struct X{ X(X&&); // move ctor #1 template<class T> X(T&&); // perfect forwarding ctor #2 X& operator=(X&&); // move assignment operator #3 template<class T> X& operator=(T&&); // perfect forwarding ass. operator #4 };
在一个真正的移动ctor(#1)和移动赋值运算符(#3)中,你永远不会使用std :: forward,因为正确地评估,你将永远移动.
请注意,没有完美的转发模板(T&&),std :: forward就没有意义. #2和#4就是这样.在这里,你永远不会使用std :: move,因为你不知道你是否真的有一个右值(A-OK)或一个左值(不是很多).
有关std :: forward实际工作原理的说明,请参见this answer of mine.