基于原地方法代理实现结构体方法:RVO/NRVO性能分析
C++结构体原地方法与非原地方法的实现疑问
我需要为C++结构体实现两类方法:一是原地(inplace)修改实例的方法,二是返回新实例、不修改原实例的常规方法。计划先实现原地方法,再通过它代理实现常规方法。以包含int成员num的结构体add方法为例,设计了两种实现方案:
方案1代码
struct myStruct_impl1{ int num = 0; void add_inplace(const int& other) { num += other; return; } myStruct_impl1 add(const int& other) const { myStruct_impl1 tmp(*this); tmp.add_inplace(other); return tmp; } };
该方案中add_inplace返回void,add方法创建临时实例tmp,调用tmp.add_inplace后返回tmp,存在触发具名返回值优化(NRVO)的可能。
方案2代码
struct myStruct_impl2{ int num = 0; myStruct_impl2& add_inplace(const int& other) { num += other; return *this; } myStruct_impl2 add(const int& other) const { return myStruct_impl2(*this).add_inplace(other); } };
该方案中add_inplace返回*this的左值引用,add方法通过return myStruct_impl2(*this).add_inplace(other)返回结果。
疑问与解答
1. 方案2中返回左值引用的结果是否会被视为临时对象,C++17后是否能保证返回值优化(RVO)?
方案2里,myStruct_impl2(*this)是一个临时右值对象,调用add_inplace后返回的是这个临时对象的左值引用。虽然返回的是引用,但这个引用绑定的是临时对象,C17的强制复制消除规则会识别这个场景:本质上我们是要返回这个临时对象,编译器可以直接把临时对象构造到函数返回值的目标内存位置,完全消除拷贝,不需要调用拷贝构造函数。也就是说,C17后这个场景是可以保证RVO的,不会产生额外拷贝。
2. 测试显示方案2触发两次拷贝构造,但Godbolt中二者汇编代码一致,该如何理解?
这种矛盾通常是编译优化开关差异导致的:
- 本地测试大概率是在Debug模式下运行的,Debug模式下编译器会禁用RVO/NRVO等优化,目的是保留代码的原始结构方便调试,所以会执行显式的拷贝构造,触发两次拷贝(一次是构造临时对象时的拷贝,一次是返回临时对象时的拷贝)。
- 而Godbolt默认使用Release模式(开启最高级优化,比如
-O2或-O3),编译器会执行复制消除,把方案2的代码优化成和方案1完全等价的汇编——直接在返回值位置构造修改后的对象,没有任何拷贝操作,所以两者汇编一致。
另外还要检查测试代码:如果没有显式定义拷贝构造函数并添加打印逻辑,编译器生成的默认拷贝构造可能不会输出信息,或者某些编译器在Debug模式下会插入额外的调试代码,导致看起来像是触发了多次拷贝,但实际在优化后这些拷贝都会被消除。
内容的提问来源于stack exchange,提问作者Pana
相关产品推荐
相关产品推荐

