对于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的效果:对象要返回直接构造代替在调用者。
如何?
传统上,呼叫方(main
这里)会预留堆栈的返回值上的一些空间:返回槽;被调用者(在这里为create
)被传递(以某种方式)返回槽的地址以将其返回值复制到。然后,被调用者为其构建结果的局部变量(如其他任何局部变量)分配自己的空间,然后在return
语句中将其复制到返回位置。
当编译器从代码中推导出该变量可以直接构建到具有等效语义(as-if规则)的返回插槽中时,触发RVO。
注意,这是这样一个共同的优化,这是明确的标准白名单和编译器不担心副本可能产生的副作用(或移动)构造函数。
什么时候?
编译器是最有可能使用简单的规则,如:
// 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
)。
一条简单的拇指法则是这样认为:如果返还口没有其他候选人被前return
语句声明
RVO应适用。
移动语义:http://www2.research.att.com/~bs/C++0xFAQ.html#rval – chris
它不一定会创建一个副本。 NRVO或移动语义可以防止这种情况。 –
您可以依靠您的编译器来执行NRVO魔术或明确使用移动语义。 –