移动赋值运算符和移动构造函数之间的区别?

Difference between the move assignment operator and move constructor?

一段时间以来,这一直让我感到困惑。到目前为止,我还没有找到满意的答案。问题很简单。 move assignment operator 什么时候被调用,move constructor operator 什么时候被调用?

cppreference.com 上的代码示例产生了以下有趣的结果:

The move assignment operator:

a2 = std::move(a1); // move-assignment from xvalue

The move constructor:

A a2 = std::move(a1); // move-construct from xvalue

那么它与实现的有什么关系呢?如果是的话,如果两者都实现了,执行哪个?为什么有可能创建一个移动赋值运算符重载,如果它无论如何都是相同的。

移动构造函数仅在构造对象时执行。对先前构造的对象执行移动赋值运算符。与复制案例中的场景完全相同。

Foo foo = std::move(bar); // construction, invokes move constructor
foo = std::move(other); // assignment, invokes move assignment operator

如果您没有显式声明它们,编译器会为您生成它们(除了一些例外,其列表太长而无法在此处发布)。

有关何时隐式生成移动成员函数的完整答案,请参阅 this

这与正常的复制赋值和复制构造相同。

A a2 = std::move(a1);
A a2 = a1;

那些调用 move/copy 构造函数,因为 a2 还不存在,需要构造。分配没有意义。这种形式称为复制初始化。

a2 = std::move(a1);
a2 = a1;

那些调用move/copy赋值运算符的,因为a2已经存在,所以构造它没有意义。

When does a move assignment operator get called

当您将 rvalue 分配给一个对象时,就像您在第一个示例中所做的那样。

and when does a move constructor operator get called?

当您使用 rvalue 初始化对象时,就像您在第二个示例中所做的那样。虽然不是运营商。

So has it do to with which is implemented?

不是,那是决定能不能用,而不是什么时候能用。例如,如果没有移动构造函数,那么构造函数将使用复制构造函数(如果存在),否则会失败(并出现错误)。

And if so which is executed if both are implemented?

赋值运算符,初始化构造函数。

And why is there the possibility of creating a move assignment operator overload at all, if it's identical anyway.

不完全相同。它是在一个已经存在的对象上调用的;调用构造函数来初始化一个以前不存在的对象。他们经常不得不做不同的事情。例如,赋值可能必须删除一些在初始化期间不存在的东西。

在以下期间调用移动构造函数:

  • 初始化:T a = std::move(b);或 T a(std::move(b));,其中 b 的类型为 T;
  • 函数参数传递:f(std::move(a));,其中a是T类型,f是void f(T t);

在以下期间调用移动赋值操作:

  • 函数return:return一个;在诸如 T f() 之类的函数中,其中 a 是具有移动构造函数的 T 类型。
  • 作业

以下示例代码说明了这一点:

#include <iostream>
#include <utility>
#include <vector>
#include <string>
using namespace std;
class A {
    public :
    A() { cout << "constructor called" << endl;}
    ~A() { cout << "destructor called" << endl;}
    A(A&&) {cout << "move constructor called"<< endl; return;}
    A& operator=(A&&) {cout << "move assignment operator called"<< endl; return *this;}

};
A fun() {
    A a; // 5. constructor called
    return a; // 6. move assignment operator called
    // 7. destructor called on this local a
}
void foo(A){
    return;
}
int main()
{
    A a; // 1. constructor called 
    A b; // 2. constructor called
    A c{std::move(b)}; // 3. move constructor called
    c = std::move(a); // 4. move assignment operator called
    a = fun();
    foo(std::move(c)); // 8. move constructor called

}

输出:

constructor called
constructor called
move constructor called
move assignment operator called
constructor called
move assignment operator called
destructor called
move constructor called
destructor called
destructor called
destructor called
destructor called