2012-12-23 68 views
11

可能重複:
What happens if I return literal instead of declared std::string?理解C++ 11個右值,移動語義和性能

考慮下面的代碼

1| string getName() { 
2|  return "meme"; 
3| } 
4| 
5| string name = getName(); 

函數getName()返回一個臨時對象。在C++ 03中,我明白「string」的拷貝構造函數被調用並且臨時對象被銷燬。實際上,編譯器(至少在gcc 4.7中)通過不創建對象「name」而是用臨時對象本身替換它並且不銷燬臨時對象(我嘗試了MyVector類;而不是std :: string)來優化第5行。

按照C++ 11標準中的定義,
1.是getName()返回一個右值嗎?
2.在上面第5行中,哪個字符串的構造函數被調用(移動或複製)?我是否應該爲移動構造函數調用std :: move()來調用?
3.使用移動語義,效率比編譯器提供的「copy elision」優化效率低嗎?

+2

這將是_copy elision _... –

+0

@ K-ballo感謝您的啓發。 –

+0

添加行號看起來很棒!從來沒有見過之前=) – qwertz

回答

18
  1. 功能不右值或左值。值類別適用於表達式。所以稱爲函數可能是一個右值或左值。在這種情況下,表達式getName()是一個右值表達式,因爲函數getName按值返回一個對象。這來自§5.2.2/ 10:

    函數調用是一個左值如果結果類型是左值引用類型或右值引用的功能的類型,一個x值如果結果類型是右值參照對象類型,否則爲

    你的函數結果類型不是左值或右值引用,所以函數調用是一個prvalue。 prvalue表達式是rvalue表達式的一個子集。

  2. 移動構造函數將被使用(除非它被刪除,它可能是)。這是因爲getName()是一個右值,因此採用右值引用的std::string的構造函數將更好地匹配參數。請注意,即使移動構造被刪除,移動構造函數仍然可以訪問。也就是說,代碼必須是可編譯的,即使它沒有被忽略。

  3. 通常,複製或移動省略的優化將完全消除任何複製或移動。所以當然比實際行動要快。如果不採取行動,字面上什麼都不會發生。將不會有爲此舉發出的代碼。編譯器通過直接構建將被複制或移動到的位置來實現此目的。

值得一提的是,這也可以等價優化:

string getName() { 
    std::string str("meme"); 
    return str; 
} 

string name = getName(); 

這裏,兩個動作將被省略(涉及什麼是俗稱Named Return Value Optimization)。這裏有兩點需要考慮。首先,return str;滿足複製/移動省音(§12.8/ 31)的準則:

複製/移動操作的此省音,稱爲複製省略,在下列情況下是允許的(其可以被組合以消除多個副本):

    在與類返回類型的函數返回語句,當表達是一種非揮發性的自動對象的名稱(比的函數與另一個或catch子句參數)
  • 與函數返回類型相同的cv-unqualified類型,可以通過直接構造自動對象int來省略複製/移動操作Ø函數的返回值
  • ...

二是,雖然str是一個左值,它仍然從因爲它符合該標準賦予了特殊的情況下移動(§12.8/ 32 ):

當的複製操作的省音的標準被滿足或一個事實,即所述源對象是一個功能參數,所述對象被複制是由一個左值指定將被滿足節省,重載解析爲副本選擇構造函數首先執行,就好像對象被指定爲b一樣是一個右值。