【问题标题】:Difference between the move assignment operator and move constructor?移动赋值运算符和移动构造函数之间的区别?
【发布时间】:2025-12-10 06:20:07
【问题描述】:

一段时间以来,这一直让我感到困惑。到目前为止,我还没有找到满意的答案。问题很简单。 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

那么它与哪个实现有关?如果是这样,如果两者都实施,则执行哪个?以及为什么有可能创建一个移动赋值运算符重载,如果它是相同的。

【问题讨论】:

    标签: c++ c++11 constructor move-semantics


    【解决方案1】:

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

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

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

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

    【讨论】:

      【解决方案2】:

      何时调用移动赋值运算符

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

      什么时候调用移动构造器操作符?

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

      那么它与哪个实现有关系?

      不,这决定了它是否可以使用,而不是它何时可以使用。例如,如果没有移动构造函数,则构造将使用复制构造函数(如果存在),否则会失败(出现错误)。

      如果是这样,如果两者都实现了,会执行哪个?

      赋值操作符,初始化构造函数。

      如果它是相同的,为什么有可能创建一个移动赋值运算符重载。

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

      【讨论】:

        【解决方案3】:

        这与正常的复制分配和复制构造相同。

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

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

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

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

        【讨论】:

          【解决方案4】:

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

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

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

          • 函数返回:返回一个;在诸如 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
          

          【讨论】:

          • “函数参数传递”是“初始化”的一个特例,我不确定你所说的“函数返回”是什么意思。
          • return a; 不是 mote-assignment
          • @M.M:请帮助我理解这一点,因为打印输出 6(来自 fun() 声明内部)是调用的移动赋值运算符。
          • a = fun(); 将返回值移动分配给areturn a; 行从局部变量 a 移动构造返回值。这两个操作都可能被省略
          最近更新 更多