c++ move赋值首先触发move构造函数

C++ move assignment triggers move constructor first

本文关键字:move 构造函数 赋值 c++      更新时间:2023-10-16
#include <iostream>    
class C {
public:
  ~C() { std::cout << this << " destructorn"; }
  C() { std::cout << this << " constructorn"; }
  C(C&& rhs) {
    std::cout << &rhs << " rhsn";
    std::cout << this << " move constructorn";
  }
  C& operator=(C&& rhs) {
    std::cout << &rhs << " rhsn";
    std::cout << this << " move assignmentn";
    return *this;
  }
};
C make_one() {
  C tmp;
  return tmp;
}
int main() {
  std::cout << "move constructor:n";
  C c1(make_one());
  std::cout << &c1 << " &c1nn";
  std::cout << "move assignment:n";
  C c2;
  c2 = make_one();
  ...
}
输出:

move constructor:
000000000021F9B4 constructor         // tmp constructed in make_one()
000000000021F9B4 rhs                 // return triggers tmp being passed to ...
000000000021FA04 move constructor    // ... c1's move constructor (see below)
000000000021F9B4 destructor          // tmp destructs on going out of scope
000000000021FA04 &c1                 // (confirmed c1's address)
move assignment:
000000000021FA24 constructor         // c2 constructed
000000000021F9B4 constructor         // tmp constructed in make_one() again
000000000021F9B4 rhs                 // tmp passed to ...
000000000021FA34 move constructor    // ... a new object's move constructor
000000000021F9B4 destructor          // tmp destructs on going out of scope
000000000021FA34 rhs                 // new object passed to ...
000000000021FA24 move assignment     // .. c2's move assignment operator
000000000021FA34 destructor          // new object destructs
...

move赋值似乎首先触发move构造函数并创建一个额外的对象。这正常吗?我希望(通过类比复制赋值)tmp直接传递给c2的move赋值。

[Visual Studio Express 2013]

这个"额外对象"叫做返回值。当从函数返回值时;这个值是从您提供给return语句的值构造的copy/move。

通常会经历copy省略,这可能解释了为什么你没有认出它。当复制省略发生时,C tmp;行实际上会直接将tmp构造成返回值。复制省略也可能发生在其他一些情况下;有关全文,请参阅c++ 11 [class.copy]#31。

假设您在这里手动禁用了复制省略,或者编译器认为不执行复制省略是一个好主意。更新:你的编译器只在发布版本上做这种特殊的复制省略-感谢Praetorian

我已经完成了我的例子,把它留在这里,以防它对其他人有帮助。

#include <iostream>
class C {
public:
  ~C() { std::cout << this << " destructorn"; }
  C() { std::cout << this << " constructorn"; }
  C(const C& rhs) {
    std::cout << &rhs << " rhsn";
    std::cout << this << " copy constructorn";
  }
  C& operator=(const C& rhs) {
    std::cout << &rhs << " rhsn";
    std::cout << this << " copy assignmentn";
    return *this;
  }
  C(C&& rhs) {
    std::cout << &rhs << " rhsn";
    std::cout << this << " move constructorn";
  }
  C& operator=(C&& rhs) {
    std::cout << &rhs << " rhsn";
    std::cout << this << " move assignmentn";
    return *this;
  }
};
C make_one() {
  C tmp;
  return tmp;
}
int main() {
  std::cout << "c1's constructor:n";
  C c1;
  std::cout << "nc1 passed to c2's copy constructor:n";
  C c2(c1);
  std::cout << &c2 << " &c2nn";
  std::cout << "c3 and c4's constructors:n";
  C c3, c4;
  std::cout << "c3 passed to c4's copy assignment:n";
  c4 = c3;
  std::cout << "ntmp constructed, passed to c5's move constructor then destructedn";
  C c5(make_one());
  std::cout << &c5 << " &c5nn";
  std::cout << "c6's constructor:n";
  C c6;
  std::cout << "tmp constructed, passed to return value's move constructor then destructedn"
               "return value passed to c6's move assignment then destructed:n";
  c6 = make_one();
  return 0;
}
调试输出:

c1's constructor:
000000000013F9B4 constructor
c1 passed to c2's copy constructor:
000000000013F9B4 rhs
000000000013F9D4 constructor
000000000013F9D4 &c2
c3 and c4's constructors:
000000000013F9F4 constructor
000000000013FA14 constructor
c3 passed to c4's copy assignment:
000000000013F9F4 rhs
000000000013FA14 copy assignment
tmp constructed, passed to c5's move constructor then destructed
000000000013F964 constructor
000000000013F964 rhs
000000000013FA34 move constructor
000000000013F964 destructor
000000000013FA34 &c5
c6's constructor:
000000000013FA54 constructor
tmp constructed, passed to return value's move constructor then destructed
return value passed to c6's move assignment then destructed:
000000000013F964 constructor
000000000013F964 rhs
000000000013FA64 move constructor
000000000013F964 destructor
000000000013FA64 rhs
000000000013FA54 move assignment
000000000013FA64 destructor
...

释放输出(显示move构造函数的省略):

c1's constructor:
00000000001BFC41 constructor
c1 passed to c2's copy constructor:
00000000001BFC41 rhs
00000000001BFC40 constructor
00000000001BFC40 &c2
c3 and c4's constructors:
00000000001BFC78 constructor
00000000001BFC70 constructor
c3 passed to c4's copy assignment:
00000000001BFC78 rhs
00000000001BFC70 copy assignment
tmp constructed, passed to c5's move constructor then destructed
00000000001BFC68 constructor
00000000001BFC68 &c5
c6's constructor:
00000000001BFC60 constructor
tmp constructed, passed to return value's move constructor then destructed
return value passed to c6's move assignment then destructed:
00000000001BFC42 constructor
00000000001BFC42 rhs
00000000001BFC60 move assignment
00000000001BFC42 destructor
....