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 进行授权