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

基于原地方法代理实现结构体方法: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 09:52:03