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

为什么C++的guaranteed copy elision无法链式生效?

为什么C++的guaranteed copy elision无法链式生效?

咱们先拆解一下你的代码里的三个场景,再一步步说清楚为什么第三个案例的复制消除没生效——其实核心是强制复制消除(guaranteed copy elision)的生效条件是有明确边界的,不是简单的“链式组合”就能触发。

先回忆C++17里强制复制消除的两个核心触发场景:

  • 当你用**纯右值(prvalue)**直接初始化一个对象时(比如函数返回prvalue给变量,或者用临时对象作为函数参数),编译器会直接把这个prvalue的“构造目标”指向最终的内存位置,完全跳过中间的拷贝/移动。
  • 函数返回一个prvalue(比如return {};)时,会直接在调用方的目标位置构造对象,不需要拷贝。

现在逐个看你的案例:

  1. auto s0 = make_s();:make_s()返回的是prvalue(return {};),所以编译器直接在s0的内存位置构造对象,完全没有拷贝——这就是你看到的第一行ctor,对应s0的构造。
  2. add_1({});:{}是prvalue,用来初始化add_1的参数s,编译器直接在函数参数的内存位置构造这个临时对象,跳过拷贝——所以输出里的ctor是这个参数的构造,然后函数里修改后输出43。
  3. auto s2 = add_1_to({});:这一步你以为是前两步的组合,但其实中间有个“断点”:
    • 第一步:{}作为prvalue初始化add_1_to的参数s——这一步确实触发了复制消除,临时对象直接构造在s的内存里(对应输出里的ctor)。
    • 第二步:函数add_1_to返回的是命名变量s,它是一个左值(有名字的对象,存在于函数的栈帧里),不是prvalue!这时候函数返回的是左值的拷贝,而强制复制消除只对prvalue生效——这时候能依赖的只有NRVO(命名返回值优化),但NRVO是编译器的可选优化,不是C++标准强制要求的!
    • 你的编译器没有做NRVO,所以就出现了copy的输出:把函数栈帧里的s拷贝到调用方的临时对象,最后这个临时对象再初始化s2(这一步其实触发了强制复制消除,所以没有额外的拷贝)。

那为什么这时候不能“链式”消除?核心原因是:
函数参数s的内存是在add_1_to的栈帧里的,函数调用结束后栈帧会被销毁,编译器不可能把这个栈帧里的内存直接复用给main函数里的s2——这会导致内存生命周期冲突。而返回prvalue的时候,编译器可以直接在调用方的目标位置构造,因为prvalue本身没有固定的内存位置;但返回左值的时候,这个左值已经有了自己的内存(栈帧里),必须拷贝出来。

如果想让第三个案例也触发强制复制消除,你可以把函数改成返回prvalue,比如:

S add_1_to(S s) {
  s.data++;
  return S{s.data}; // 返回prvalue,不是左值
}

这时候编译器会直接在s2的内存位置构造这个返回的prvalue,完全跳过中间的拷贝——你再跑代码就不会看到copy的输出了。

备注:内容来源于stack exchange,提问作者Ralph Zhang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 15:13:02