为何X x(X{});仅调用默认构造而非默认+移动构造?
问题分析与解释
代码示例
class X { public: X(int x):m_x(new int{x}) { cout << this << '\t'; cout << "Constuctor X(int x)"<<endl; } X() { cout << this << '\t'; cout << "Constuctor X()"<<endl; } X(const X& x) { cout << this << '\t'; cout << "Constuctor X(const X&)"<<endl; cout << "addr of const X& x = " << &x <<endl; } X(X&& x) { cout << this << '\t'; cout << "Constuctor X(X&&)"<<endl; cout << "addr of X&& x = " << &x <<endl; this->m_x = x.m_x; x.m_x = nullptr; } X& operator=(const X& x) { cout << this << '\t'; cout << "= copy operator"<<endl; m_x = x.m_x; } X& operator=(X&& x){ cout << this << '\t'; cout << "= move operator"<<endl; } X& operator++(){ cout << this << '\t'; *m_x++; cout << "++ operator"<<endl; return *this; } ~X() { cout << this << '\t'; cout << "Destructor"<<endl; if(m_x) delete m_x; else cout << "m_x null\t"; } int* m_x = nullptr; }; int main() { X x(X{}); }
程序输出
0x7ffcc5a0b150 Constuctor X() 0x7ffcc5a0b150 Destructor m_x null
原因解释
你遇到的是C++标准明确允许的复制消除(Copy Elision),具体属于临时对象消除的优化场景。
按照未优化的逻辑,X x(X{});确实会先调用默认构造创建临时对象X{},再通过移动构造函数用这个临时对象初始化x。但编译器有权直接跳过临时对象的创建步骤,直接在x的内存空间上执行默认构造——相当于把代码优化成了X x;,这样就省去了临时对象的构造、移动构造和析构流程,所以你只看到一次默认构造和对应的析构。
这种优化优先级极高,哪怕你的移动/复制构造函数包含打印日志这类副作用,编译器也可以选择执行该优化。如果想验证原本的执行流程,可以通过编译参数禁用该优化:
- GCC/Clang:添加
-fno-elide-constructors - MSVC:添加
/Od(禁用所有优化)
启用禁用参数后,程序会按照你预期的流程运行:先构造临时对象,再调用移动构造初始化x,最后分别析构临时对象和x。
内容的提问来源于stack exchange,提问作者Tien Dollars
相关产品推荐
相关产品推荐

