對於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魔術或明確使用移動語義。 –