文章

C++返回值优化(RVO)

C++返回值优化(RVO)是编译器直接在调用者内存构造返回对象,避免拷贝或移动,提高性能的优化技术。

C++返回值优化(RVO)

C++返回值优化(RVO)

返回值优化(RVO,Return Value Optimization)是 C++ 编译器为了减少对象拷贝构造和移动构造的一种重要优化技术。理解它对写高效、无性能损失的代码很有帮助。

RVO 是什么?

当函数返回一个对象时,按照传统理解,编译器会:

  • 在函数内部创建一个临时对象(返回值对象)。
  • 通过拷贝构造函数或移动构造函数,将临时对象拷贝或移动到调用者的变量。

这可能导致一次或多次额外的对象构造、拷贝、移动,影响性能。

返回值优化(RVO)就是编译器的一个优化技术,目的是:

直接在调用者提供的存储空间中构造返回对象,省去临时对象的创建和拷贝/移动,从而提升性能。

RVO 具体例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class MyClass {
public:
    MyClass() { std::cout << "Constructed\n"; }
    MyClass(const MyClass&) { std::cout << "Copy Constructed\n"; }
    MyClass(MyClass&&) noexcept { std::cout << "Move Constructed\n"; }
};

MyClass create() {
    MyClass obj;
    return obj;
}

int main() {
    MyClass a = create();
}

没有 RVO:

  • create() 内部先构造 obj
  • 返回时调用拷贝构造(或移动构造)将 obj 拷贝/移动给 a

有 RVO:

  • 编译器直接在 a 的内存空间中构造 obj
  • 没有额外的拷贝或移动构造调用

RVO 的两种主要形式

  • NRVO(Named Return Value Optimization) 函数返回的是一个具名变量,比如上面例子中的 obj,编译器可以直接在调用者提供的空间构造 obj
  • Unnamed RVO 返回的是一个临时对象,比如 return MyClass();,编译器也可以直接在调用者空间构造该临时对象。

C++ 标准对 RVO 的支持

  • C++03:RVO 是一种允许的优化,但不是必须,编译器可选实现。
  • C++11:引入了移动语义,减少了拷贝开销,但 RVO 依然是编译器优化。
  • C++17:对某些情形下的返回值,标准强制执行所谓的拷贝省略,即RVO成为必需。

简言之,C++17以后部分情况下编译器必须进行返回值优化,禁止调用拷贝或移动构造。

RVO 如何影响代码设计?

  • 如果类型拷贝/移动代价大,RVO 能显著提升性能。
  • 可以放心写函数返回局部变量的对象,现代编译器会尽量优化。
  • 依赖 RVO 不等于不写高效的拷贝/移动构造,仍需合理设计拷贝/移动语义。
  • 若需要强制避免拷贝/移动构造,C++17 后可以依赖强制拷贝省略规则。

代码示例对比

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
MyClass f1() {
    MyClass a;
    return a;       // NRVO可能生效
}

MyClass f2() {
    return MyClass();  // 无名临时对象,RVO通常生效
}

MyClass f3() {
    MyClass a;
    MyClass b;
    if (some_condition) {
        return a;    // NRVO可能生效
    } else {
        return b;    // NRVO可能无法生效,依赖编译器
    }
}

其中 f3 中因为有多个返回变量,NRVO 可能失效,编译器会退回使用移动构造。

总结

方面说明
RVO 目的避免返回对象的临时拷贝或移动构造
形式NRVO(命名变量)和 Unnamed RVO(临时变量)
标准支持C++17 标准部分情景强制拷贝省略
性能提升减少额外拷贝构造,提升函数返回效率
开发建议允许自然写法,现代编译器会做优化,不必刻意规避
本文由作者按照 CC BY 4.0 进行授权