C++ 中的 RVO 与 NRVO
1. 什么是 RVO?
RVO(Return Value Optimization,返回值优化)是 C++ 编译器的一种优化技术,通过消除不必要的临时对象拷贝来提升性能。NRVO(Named RVO)是 RVO 的变体,针对已命名的局部变量。
关键时间点:
- C++11 之前:RVO/NRVO 作为编译器优化选项存在
- C++17:强制要求对 prvalue(纯右值)执行 RVO
- C++20:进一步扩展了强制 RVO 的场景
先来看一张图直观理解:

2. 代码示例
#include <iostream>
#include <string>
#include <utility>
class BigObject {
public:
BigObject() { std::cout << "Constructor\n"; }
~BigObject() { std::cout << "Destructor\n"; }
BigObject(BigObject const&) { std::cout << "Copy Constructor\n"; }
BigObject& operator=(BigObject const&) {
std::cout << "Assignment Operator\n";
return *this;
}
BigObject(BigObject&&) { std::cout << "Move Constructor\n"; }
};
// RVO: 返回纯右值(prvalue)
BigObject f1() { return BigObject(); }
// NRVO: 返回命名的局部对象
BigObject f2() {
BigObject obj;
return obj;
}
// 无 RVO: 返回数组元素或复杂表达式
BigObject f3() {
BigObject obj[1];
return obj[0];
}
// 无 RVO: 显式 std::move 阻止优化
BigObject f4() { return std::move(BigObject()); }
// 无 RVO: const_cast 破坏优化条件
BigObject f5() {
BigObject obj;
return const_cast<BigObject&>(obj);
}
int main() {
std::cout << "1 ------------------------" << std::endl;
{ BigObject obj1 = f1(); }
std::cout << "2 ------------------------" << std::endl;
{ BigObject obj2 = f2(); }
std::cout << "3 ------------------------" << std::endl;
{ BigObject obj3 = f3(); }
std::cout << "4 ------------------------" << std::endl;
{ BigObject obj4 = f4(); }
std::cout << "5 ------------------------" << std::endl;
{ BigObject obj5 = f5(); }
return 0;
}运行结果
1 ------------------------
Constructor
Destructor
2 ------------------------
Constructor
Destructor
3 ------------------------
Constructor
Copy Constructor
Destructor
Destructor
4 ------------------------
Constructor
Move Constructor
Destructor
Destructor
5 ------------------------
Constructor
Copy Constructor
Destructor
Destructor结果分析
| 函数 | 输出 | RVO 状态 | 原因 |
|---|---|---|---|
f1() | Constructor + Destructor | ✅ RVO 生效 | 返回纯右值(prvalue),C++17 强制优化 |
f2() | Constructor + Destructor | ✅ NRVO 生效 | 返回命名局部对象,编译器优化消除拷贝 |
f3() | 多了 Copy Constructor | ❌ 无 RVO | 返回数组元素,编译器无法确定对象来源 |
f4() | 多了 Move Constructor | ❌ 无 RVO | std::move 将对象转为 xvalue,阻止 RVO |
f5() | 多了 Copy Constructor | ❌ 无 RVO | const_cast 破坏了 RVO 的前提条件 |
2. RVO 的触发条件
必须满足的条件
- 返回类型匹配:返回的对象类型必须与函数返回类型完全一致
- 返回局部对象:对象必须是函数内的自动存储期变量
- 不能是静态变量:静态变量的生命周期超出函数作用域
- 不能有多个返回路径返回不同对象:所有 return 语句应返回同一个对象
会阻止 RVO 的操作
// ❌ 显式 std::move - 不要这样做!
BigObject bad1() {
BigObject obj;
return std::move(obj); // 阻止 NRVO,强制移动
}
// ❌ 返回参数 - 参数不是局部对象
BigObject bad2(BigObject obj) {
return obj; // 无法 RVO,但会触发移动语义
}
// ❌ 返回全局/静态变量
BigObject global;
BigObject bad3() {
return global; // 无法 RVO
}
// ❌ 条件返回不同对象
BigObject bad4(bool cond) {
BigObject a, b;
return cond ? a : b; // 无法 NRVO
}3. 最佳实践
// ✅ 正确:让编译器自动优化
BigObject good1() {
BigObject obj;
return obj; // 编译器会自动应用 NRVO
}
// ✅ 正确:直接返回临时对象
BigObject good2() {
return BigObject(); // C++17 强制 RVO
}
// ✅ 正确:使用工厂模式
BigObject createObject(int type) {
if (type == 1) {
return BigObject(); // RVO
}
return BigObject(); // RVO
}4. 核心建议
- 永远不要对返回值使用
std::move:这会阻止 NRVO,反而降低性能 - 信任编译器:现代编译器(GCC、Clang、MSVC)都能很好地执行 RVO/NRVO
- 理解 C++17 的变化:prvalue 的返回现在是强制优化的,不再依赖编译器
- 保持代码简洁:简单的返回语句更容易被优化



