2015-07-20 38 views
3

考慮下面的代碼中的一員:如何返回本地對象

struct Foo 
{ 
    Foo() { cout << "Foo()\n"; } 
    ~Foo() { cout << "~Foo()\n"; } 
    Foo(Foo&) { cout << "Foo(Foo&)\n"; } 
    Foo(Foo&&) { cout << "Foo(Foo&&)\n"; } 

    int d; 
}; 

struct Bar 
{ 
    Foo bigData; 
    void workOnBigData() { /*...*/ } 
} 

Foo getBigData() 
{ 
    Bar b; 
    b.workOnBigData(); 
    return b.bigData; 
} 

什麼是落實getBigData()在副本ellision方面/移動語義的最佳方式?在這個實現中,編譯器似乎不被允許移動bigData。我測試了以下功能:

Foo f() 
{ 
    Foo foo; 
    return foo; // RVO 
} 

Foo g() 
{ 
    Bar b; 
    return b.bigData; // Copy 
} 

Foo h() 
{ 
    Bar b; 
    auto r = move(b.bigData); 
    return r; // Move 
} 

你能解釋這些實現的結果,並顯示返回本地對象的成員的最有效途徑。

+4

對於命名返回值優化,調用者調用之前保留一些堆棧空間功能。這個內存然後將被用來*在*中構造被調用者的本地對象,return語句變成了no-op。因此,NRVO不能應用於子對象,只能用於完成對象(它有其他限制)。 – dyp

+4

自動*返回*是一個優化,它改變了程序的可觀察行爲。它被保守地引入,即僅適用於NRVO被允許應用(但編譯器無法)和其他一些特殊情況的情況。如果沒有引入,那麼人們通常會寫'return std :: move(x);'即使''''可以通過NRVO返回;但'move(x)'防止NRVO,所以這將是一個悲觀(對於移動類型,如果應用NRVO,'return x''還必須檢查移動ctor)。 – dyp

+0

@dyp是否意味着,每當NRVO無法應用時,應該使用return std :: move(x)來強制執行返回操作(在這種情況下不能自動完成)? – hansmaad

回答

3

有很多的方法,以避免額外的副本,一個更接近你的代碼將是恕我直言:

Foo getBigData() 
{ 
    Foo ret; // do a cheap initialization 
    Bar b; 
    b.workOnBigData(); 

    std::swap(ret, b.bigData); // 'steal' the member here 

    return ret; // NRVO can apply 
} 

通過移動構造返回對象

Foo getBigData() 
{ 
    Bar b; 
    b.workOnBigData();  
    Foo ret(std::move(b.bigData)); // these two lines are equivalent to 
    return ret;     // return std::move(b.bigData); 
} 
1

我想實現同樣可以回答這個問題Why isn't the copy constructor elided here?在回答你的問題時非常有用。複製省略不是在你的例子

Foo getBigData() 
{ 
    Bar b; 
    b.workOnBigData(); 
    return b.bigData; 
} 

,因爲這requiment不fullfiled(http://en.cppreference.com/w/cpp/language/copy_elision)使用:

return語句的表達是一種非易失性對象 具有自動存儲時間的名稱。 .. 並且其具有相同的類型(忽略頂層CV-資質)作爲 函數的返回類型,然後複製/移動省略

在您的示例中,Bar是一個具有自動存儲持續時間的變量,但返回Foo。如果你改變你的酒吧類的編譯器將開始使用複製省略:

#include <iostream> 
#include <typeinfo> 

using namespace std; 

struct Foo 
{ 
    Foo() { cout << "Foo()\n"; } 
    ~Foo() { cout << "~Foo()\n"; } 
    Foo(const Foo&) { cout << "Foo(Foo&)\n"; } 
    Foo(Foo&&) { cout << "Foo(Foo&&)\n"; } 

    int d; 
}; 

struct Bar 
{ 
    Foo bigData; 
    void workOnBigData() { /*...*/ } 
}; 

struct Bar2 
{ 
    void workOnBigData(Foo&) { /*...*/ } 
}; 

Foo getBigData() 
{ 
    Bar b; 
    b.workOnBigData(); 
    return b.bigData; 
} 

Foo getBigData2() 
{ 
    Foo f; 
    Bar2 b; 
    b.workOnBigData(f); 
    return f; 
} 

int main() 
{ 
    { 
    Foo f = getBigData(); 
    } 
cout << "---" << endl; 

    { 
    Foo f = getBigData2(); 
    } 
} 

#include <iostream> 
#include <typeinfo> 

using namespace std; 

struct Foo 
{ 
    Foo() { cout << "Foo()\n"; } 
    ~Foo() { cout << "~Foo()\n"; } 
    Foo(const Foo&) { cout << "Foo(Foo&)\n"; } 
    Foo(Foo&&) { cout << "Foo(Foo&&)\n"; } 

    int d; 
}; 

struct Bar 
{ 
    Foo bigData; 
    void workOnBigData() { /*...*/ } 
}; 

struct Bar2 
{ 
    void workOnBigData(Foo&) { /*...*/ } 
}; 

Foo getBigData() 
{ 
    Bar b; 
    b.workOnBigData(); 
    return b.bigData; 
} 

Foo getBigData2() 
{ 
    Foo f; 
    Bar2 b; 
    b.workOnBigData(f); 
    return f; 
} 

int main() 
{ 
    { 
    Foo f = getBigData(); 
    } 
cout << "---" << endl; 

    { 
    Foo f = getBigData2(); 
    } 
} 

這就是它輸出:

$ ./a.out  
Foo() 
Foo(Foo&) 
~Foo() 
~Foo() 
--- 
Foo() 
~Foo()