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

运算符重载:修改临时对象还是创建新对象?

临时对象优化与operator+/-=效率的疑问解答

先还原问题中的代码场景:

class A {
public:
    A(const A&);
    A(A &&);
    ~A();

    A &operator += (const A &);
    A operator + (const A &);
private:
    class B;
    B *b;
};

A factory();
void sink(const A &);

void foo(const A &x) {
    sink(factory() += x); // 高亮行
}

一、用operator+=替代operator+的优化是否合理?

这个优化是合理的。核心逻辑差异在于:

  • factory() += x:factory()返回的临时右值对象会被直接修改(比如更新B指向的内部资源),全程仅存在这一个临时对象,最终由sink的const引用绑定,仅触发一次析构。
  • 常规实现的factory() + x:operator+会先拷贝构造一个新临时对象(以factory()的返回值为模板),再对新对象执行+=操作后返回。即使有移动语义,也会多一次临时对象的构造(或移动构造)与析构,当A的内部资源(比如B*指向的堆内存)较大时,额外开销会很明显。

你通过Godbolt观察到的汇编差异,正是这种逻辑差异的直接体现。

二、两种写法的利弊对比

operator+=写法

  • 优势:无额外临时对象开销,对于资源密集型的A类,能显著减少内存分配、复制的次数,效率更高。
  • 劣势:语义违背直觉,factory() += x看起来像是修改了factory返回的临时对象,不符合+操作“不修改原操作数”的常规认知,可读性差;后续如果需要链式操作(比如factory() += x += y),逻辑会更混乱。

operator+写法

  • 优势:语义清晰,符合数学运算的直觉,代码可读性、可维护性更高;链式操作(factory() + x + y)逻辑自然,符合用户预期。
  • 劣势:常规实现下会多一次临时对象的构造/析构,带来额外性能开销,当A的复制成本很高时,这个开销不可忽略。

三、如何保留operator+写法同时达到同等效率?

可以通过优化operator+的实现,消除额外临时对象的开销,主要有两种方案:

1. 为operator+提供右值重载版本

利用右值引用直接复用factory()返回的临时对象资源:

// 非成员函数,覆盖左值+左值、右值+左值的情况
A operator+(const A& lhs, const A& rhs) {
    A temp(lhs);
    temp += rhs;
    return temp;
}

A operator+(A&& lhs, const A& rhs) {
    lhs += rhs;
    return std::move(lhs);
}

当调用factory() + x时,factory()返回的右值会匹配第二个重载:直接在该右值对象上执行+=,再通过移动构造返回(A的移动构造是浅拷贝B*,几乎无开销)。结合编译器的返回值优化(RVO),最终的执行逻辑和factory() += x完全一致,无额外临时对象。

2. 利用C++17的保证复制消除(Guaranteed Copy Elision)

C++17标准强制要求编译器消除某些场景下的复制/移动操作,我们可以调整operator+的参数传递方式:

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

当传入右值(factory()的返回值)时,lhs会通过移动构造初始化(而非拷贝),执行+=后返回lhs,此时C++17的保证复制消除会直接把lhs的资源转移到最终的临时对象中,全程仅存在一次临时对象,效率和operator+=写法持平。

另外,还可以结合完美转发实现更通用的operator+,适配左值、右值等多种场景:

template<typename T, typename U>
std::enable_if_t<std::is_same_v<std::decay_t<T>, A>, A>
operator+(T&& lhs, const U& rhs) {
    std::forward<T>(lhs) += rhs;
    return std::forward<T>(lhs);
}

这些优化的核心前提是:A的移动构造函数是高效的(比如仅浅拷贝指针、无额外资源分配)。如果移动构造本身开销较大,需要进一步调整类的内部实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 13:10:58