在移动运算符/赋值运算符中,我应该使用std::move还是std::forward



除非我错了,否则两者似乎都很好用——有没有最好的实践理由来选择其中一个?

示例:

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
};

实际移动操作符(#1)和移动赋值操作符(#3)中,您永远不会使用std::forward,因为如果您正确评估,您将始终移动。

注意,如果没有完美的转发模板(T&&),std::forward就没有意义。这正是#2和#4的情况。在这里,你永远不会使用std::move,因为你不知道你是否真的得到了一个右值(A-OK)或一个左值(不那么多)。

请参阅我的答案,了解std::forward的实际工作原理。

最新更新