对于 RVO(返回值优化)的工作原理似乎有些混乱。
一个简单的例子:
#include <iostream>
struct A {
int a;
int b;
int c;
int d;
};
A create(int i) {
A a = {i, i+1, i+2, i+3 };
std::cout << &a << "\n";
return a;
}
int main(int argc, char*[]) {
A a = create(argc);
std::cout << &a << "\n";
}
其输出为ideone:
0xbf928684
0xbf928684
令人惊讶吗?
其实这就是RVO的效果:物体待退回是直接构造的in place在呼叫者中。
How ?
传统上,调用者 (main
这里)将在堆栈上为返回值保留一些空间:返回槽;被调用者(create
这里)(以某种方式)传递了返回槽的地址以将其返回值复制到其中。然后,被调用者为局部变量分配自己的空间,在其中构建结果,就像任何其他局部变量一样,然后将其复制到返回槽中return
陈述。
当编译器从代码中推断出该变量可以直接构造到返回槽具有等效的语义(as-if 规则)。
请注意,这是一种常见的优化,它已被标准明确列入白名单,并且编译器不必担心复制(或移动)构造函数可能产生的副作用。
When ?
编译器最有可能使用简单的规则,例如:
// 1. works
A unnamed() { return {1, 2, 3, 4}; }
// 2. works
A unique_named() {
A a = {1, 2, 3, 4};
return a;
}
// 3. works
A mixed_unnamed_named(bool b) {
if (b) { return {1, 2, 3, 4}; }
A a = {1, 2, 3, 4};
return a;
}
// 4. does not work
A mixed_named_unnamed(bool b) {
A a = {1, 2, 3, 4};
if (b) { return {4, 3, 2, 1}; }
return a;
}
在后一种情况 (4) 中,当以下情况时不能应用优化:A
由于编译器无法构建而返回a
在返回槽中,因为它可能需要它来做其他事情(取决于布尔条件b
).
一个简单的经验法则是:
如果在返回时间之前没有宣布其他候选人,则应适用 RVOreturn
陈述。