为什么C++的guaranteed copy elision无法链式生效?
为什么C++的guaranteed copy elision无法链式生效?
咱们先拆解一下你的代码里的三个场景,再一步步说清楚为什么第三个案例的复制消除没生效——其实核心是强制复制消除(guaranteed copy elision)的生效条件是有明确边界的,不是简单的“链式组合”就能触发。
先回忆C++17里强制复制消除的两个核心触发场景:
- 当你用**纯右值(prvalue)**直接初始化一个对象时(比如函数返回prvalue给变量,或者用临时对象作为函数参数),编译器会直接把这个prvalue的“构造目标”指向最终的内存位置,完全跳过中间的拷贝/移动。
- 函数返回一个prvalue(比如
return {};)时,会直接在调用方的目标位置构造对象,不需要拷贝。
现在逐个看你的案例:
auto s0 = make_s();:make_s()返回的是prvalue(return {};),所以编译器直接在s0的内存位置构造对象,完全没有拷贝——这就是你看到的第一行ctor,对应s0的构造。add_1({});:{}是prvalue,用来初始化add_1的参数s,编译器直接在函数参数的内存位置构造这个临时对象,跳过拷贝——所以输出里的ctor是这个参数的构造,然后函数里修改后输出43。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
相关产品推荐
相关产品推荐

