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

C++二元算术运算符重载:传值优化与实现选择疑问

C++二元算术运算符重载的传值与传引用优化问题

我查阅了cppreference网站上关于运算符重载的「二元算术运算符」页面,对一段代码中为编译器优化鼓励传值而非传引用的注释感到困惑。按我的认知,尽可能传引用更优。

示例代码

第一个示例

class A {
   public:
   A& operator+=(A const& rhs) {
      /*...*/
      return *this;
   }

   friend A operator+(A lhs, A const& rhs) {
      lhs += rhs;
      return lhs;
   }
};

该代码说明operator+中按值传递lhs有助于优化链式加法,但有观点称传值会阻止「具名返回值优化(NRVO)」。

第二个示例

inline A operator+(A const& lhs, X const& rhs) {
  A a = lhs;
  a += rhs;
  return a;
}

相关讨论指出,若operator+基于operator+=实现,第一个示例的优化逻辑成立。

问题

    1. 若operator+不基于operator+=实现,第一个示例的优化是否仍成立?
    1. 若第二个示例支持具名返回值优化,哪种实现的计算效率更优?
    1. 还有其他选择某一种实现的理由吗?

回答

问题1

不成立。第一个示例的核心优化是利用传值时的拷贝/移动构造复用临时对象:链式调用a + b + c时,a + b返回的临时对象可直接作为下一个operator+的lhs实参,触发移动构造而非拷贝,修改后直接返回。但如果operator+不依赖operator+=,意味着你要在函数内重新构造新对象,此时传值的lhs会产生多余的拷贝开销——本来可以直接用传引用的lhs构造新对象,现在多了一次传值拷贝,且返回的新对象无法复用lhs的存储,优化自然失效。

问题2

多数场景下第一个示例的实现更优:

  • 第一个示例中,lhs是传值进来的,修改后直接返回,编译器可触发返回值优化(RVO)——返回的是函数参数本身,编译器能直接复用实参和返回值的内存,省去一次移动/拷贝。如果调用者传入临时对象,传值时会直接触发移动构造,连初始拷贝都能省。
  • 第二个示例即便有NRVO,也必须先拷贝lhs到局部对象a,再修改返回。虽然NRVO能省去返回时的拷贝,但第一步的拷贝无法避免。

比如链式调用a + b + c:

  • 第一个示例:a + b的临时对象作为下一个operator+的lhs触发移动构造,返回时触发RVO,全程最多1次拷贝+1次移动,甚至可能被优化到0次。
  • 第二个示例:每次调用都要拷贝lhs到a,即使有NRVO,也至少有2次拷贝操作。

问题3

还有这些场景可以选择对应实现:

选第一个示例(传值lhs)的理由:

  • 代码简洁:直接复用operator+=的逻辑,减少重复代码,不用手动写拷贝构造和修改逻辑。
  • 自动适配移动语义:传入临时对象时,编译器会自动用移动构造代替拷贝构造,无需额外写重载版本(比如operator+(A&& lhs, const A& rhs))。
  • 规避悬空引用风险:传值不会出现引用失效的问题,避免返回局部对象引用这类低级错误。

选第二个示例(传引用lhs)的理由:

  • 当operator+逻辑无法复用operator+=时,比如需要构造完全不同的对象,传引用可避免不必要的传值拷贝。
  • 对于禁用移动构造的类型,传值开销过大,此时传引用+NRVO是更稳妥的选择。

内容的提问来源于stack exchange,提问作者Pompan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 15:12:42