You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 17:05:21