C++11:传递临时对象给构造函数为何未调用移动构造函数?
为什么
M obj1(M{})没有触发移动构造函数? 先看测试代码:
#include<iostream> #include<utility> using namespace std; struct M{ M(){cout<<"ctor\n";} M(const M&m){cout<<"copy ctor\n";} M(M&&m){cout<<"move ctor\n";} M& operator=(const M&m){cout<<"copy operator=\n"; return *this;} M& operator=(M&&m){cout<<"move operator=\n"; return *this;} }; int main(){ M obj1(M{}); // 为何此处仅输出"ctor"而非"move ctor"? cout << "----\n"; M obj2(move(M{})); // 如预期输出ctor和move ctor return 0; }
编译运行后的输出:
ctor ---- ctor move ctor
核心原因:复制消除(Copy Elision)
这是C++标准允许的编译器优化行为,具体为临时对象消除:当用一个临时对象直接初始化同类型的新对象时,编译器有权跳过拷贝/移动构造函数,直接在新对象的内存空间上构造这个临时对象。
对于M obj1(M{}):
M{}是一个纯右值临时对象,用来直接初始化obj1,完全符合复制消除的条件。编译器会直接把临时对象的构造过程放到obj1的内存地址上,相当于直接执行M obj1;的逻辑,所以只会输出ctor,不会触发移动构造。
而M obj2(move(M{}))的情况不同:
move(M{})返回的是M&&类型的右值引用,它是对临时对象的引用,而非临时对象本身。此时编译器无法进行复制消除——因为初始化表达式是引用,不是可被消除的临时对象,所以必须先构造临时对象(输出ctor),再通过移动构造把它的资源转移给obj2(输出move ctor)。
验证方法:禁用复制消除
如果给编译器加上禁用复制消除的选项(Clang/GCC用-fno-elide-constructors,MSVC用/Zc:noelide),再编译运行M obj1(M{}),就会看到预期的ctor+move ctor输出,这也证明原本的行为是编译器优化导致的。
补充说明
复制消除是C++标准明确允许的优化,即使移动构造函数有可见的副作用(比如这里的cout输出),编译器也可以选择跳过它——标准认为这种优化不会改变程序的“可观测行为”(除了构造函数的副作用输出,但标准允许忽略这种情况)。
内容的提问来源于stack exchange,提问作者Troskyvs
相关产品推荐
相关产品推荐

