什么是 RVO?

RVO(Return Value Optimization,返回值优化)是 C++ 编译器的一种优化技术,通过消除不必要的临时对象拷贝来提升性能。NRVO(Named RVO)是 RVO 的变体,针对已命名的局部变量。

关键时间点:

  • C++11 之前:RVO/NRVO 作为编译器优化选项存在
  • C++17:强制要求对 prvalue(纯右值)执行 RVO
  • C++20:进一步扩展了强制 RVO 的场景

先来看一张图直观理解:

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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❌ 无 RVOstd::move 将对象转为 xvalue,阻止 RVO
f5()多了 Copy Constructor❌ 无 RVOconst_cast 破坏了 RVO 的前提条件

RVO 的触发条件

必须满足的条件

  1. 返回类型匹配:返回的对象类型必须与函数返回类型完全一致
  2. 返回局部对象:对象必须是函数内的自动存储期变量
  3. 不能是静态变量:静态变量的生命周期超出函数作用域
  4. 不能有多个返回路径返回不同对象:所有 return 语句应返回同一个对象

会阻止 RVO 的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ❌ 显式 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
}

最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ✅ 正确:让编译器自动优化
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
}

核心建议

  1. 永远不要对返回值使用 std::move:这会阻止 NRVO,反而降低性能
  2. 信任编译器:现代编译器(GCC、Clang、MSVC)都能很好地执行 RVO/NRVO
  3. 理解 C++17 的变化:prvalue 的返回现在是强制优化的,不再依赖编译器
  4. 保持代码简洁:简单的返回语句更容易被优化