什麼時候應當依靠返回值優化(RVO)?
C++的函數返回vector之類或者自定義類型時會產生額外的拷貝構造函數、析構函數開銷,例如
class A{
......
};
A foo(){
......
}
int main(){
......
A c = foo();
......
}
foo()函數中的局部變數會被用來進行拷貝構造函數然後析構,以前學C++時一直說的是要避免直接返回大對象,寧可用引用或指針傳進來。但是返回值優化有時可以把這些額外的拷貝構造等開銷節省下來,所以:
- 是不是現在的C++函數在返回自定義類型或者vector等時不必再關心效率問題?
- 似乎返回值優化也有失效的時候,具體是在什麼情況下呢?
- C++11有了右值引用和移動語義,函數可以用移動語義來返回嗎,如果可以,什麼時候用移動語義,什麼什麼依賴編譯器的RVO呢?
根據effective modern c++中介紹,編譯器進行RVO條件有二
- return 的值類型與 函數簽名的返回值類型相同
- return的是一個局部對象
現在我們來考慮下面這個語句
return std::move(w)
此時返回的並不是一個局部對象,而是局部對象的右值引用。編譯器此時無法進行rvo優化,能做的只有根據std::move(w)來移動構造一個臨時對象,然後再將該臨時對象賦值到最後的目標。所以,不要試圖去返回一個局部對象的右值引用。
下面來談一下右值引用與函數之間的關係。
第一個例子:
std::vector&
{
std::vector&
return tmp;
}
std::vector&
此時,並不調用RVO,拷貝構造臨時對象,同時臨時對象的生命周期延長至與rval_ref相同,等價於下面的代碼
const std::vector&
第二個例子:
std::vector&
{
std::vector&
return std::move(tmp);
}
std::vector& 該代碼會造成一個運行時錯誤,因為rval_ref最終指向被析構了的tmp 。類似於返回了內部對象的左值引用。 第三個例子:
std::vector&
{
std::vector&
return std::move(tmp);
}
std::vector&
該例子類似於第一個例子,只不過臨時對象的構造是由右值移動構造的。
最好的例子:
std::vector&
{
std::vector&
return tmp;
}
std::vector&
該代碼會調用RVO,不生成臨時對象 ,返樸歸真了。
參考鏈接(其實完全翻譯自此鏈接):c++ - C++11 rvalues and move semantics confusion (return statement)
推薦閱讀:
※認真學完 C++ Primer 後,C++ 語言到了什麼水平?
※為什麼C++編譯器不能發現未初始化的變數?
※數據結構課本中的「生命遊戲」有哪些奇葩的玩法?
※如何理解《Effective C++》第31條將文件間的編譯依賴關係降低的方法?
※輪子哥的C++為什麼學的這麼好?請分享一下學習C++秘訣?