c++17起copy elision是强制语义而非可选优化,仅在返回非volatile局部变量(nrvo)和用纯右值初始化同类型对象时必须发生,此时拷贝/移动构造函数(含副作用)完全不被调用。

Copy Elision 不是“可选优化”,而是 C++17 起的强制语义——编译器必须跳过拷贝/移动构造,哪怕构造函数有副作用。
什么时候一定会发生 Copy Elision(C++17)
只在两种场景下,标准强制要求跳过拷贝/移动:返回局部对象(NRVO),和用临时对象初始化同类型对象(RVO 的泛化)。注意:不是“可能省”,是“不准构造”。
-
return一个与函数返回类型相同的**非 volatile 局部变量** → 编译器必须复用该对象内存,不调用拷贝/移动构造 - 用纯右值(如
MyClass()、func()返回的临时对象)直接初始化同类型对象 → 必须就地构造,跳过中间临时+拷贝两步 - 不满足上述条件?比如
return std::move(local)或返回引用/不同类型,则不触发强制 elision
为什么不能依赖拷贝构造函数的副作用
因为一旦触发强制 elision,编译器连函数体都不进——MyClass(const MyClass&) 根本不会被调用,打印日志、计数器自增、资源申请等全被跳过。
- 写日志调试时发现“拷贝构造没执行”?不是 bug,是 C++17 合法且必须的行为
- 把资源释放逻辑塞进析构、却在拷贝构造里做资源克隆?程序行为会因是否 elision 而分裂——C++17 后这种设计直接失效
- 想验证是否发生 elision?加
[[maybe_unused]]抑制未使用警告,并观察cout 是否输出
std::move 和 elision 是互斥的
显式调用 std::move 会把左值转成右值,但同时也破坏了 NRVO 的前提(NRVO 要求返回的是“未加修饰的局部变量名”),反而阻止强制 elision。
立即学习“C++免费学习笔记(深入)”;
- ✅ 正确(触发强制 elision):
MyClass make() { MyClass x; return x; } - ❌ 错误(禁用 NRVO,且不一定调用移动构造):
MyClass make() { MyClass x; return std::move(x); } - ⚠️ 即使移动构造被调用,它也可能被二次 elision(如果满足 RVO 条件),但第一层 NRVO 已丢
真正难处理的点在于:你无法在代码里“感知”elision 是否发生。它改变的是对象生命周期的语义边界,而不是性能数字——写构造/析构逻辑时,得默认它们可能被整个绕过。








