2012-06-18 54 views
7

我知道,當通過值傳遞一個對象給一個函數時,如果有一個移動構造函數總是被調用,假設沒有複製elision。關於通過值返回對象怎麼樣?對象保證在返回時被移動?

例如,假設我們有一個具有移動構造函數的類Foo,並且我們有一個返回Foo對象的函數。

Foo g() { 
    Foo f; 

    // do something with f 

    return f; 
} 

如果我們假設沒有RVO,移動構造函數是否保證被調用?

更新:我想我沒有清楚地表明我的意圖。我只想知道我可以在最壞的情況下將對象移動而不是複製。 RVO或NRVO都會發生,我很高興。而且我也應該說移動構造函數和移動賦值不會被刪除並被正確實現。

+0

是的,自動存儲中的本地對象在返回語句中被隱式視爲xvalues。 – ildjarn

+0

@ildjarn:我認爲只有當你直接返回它。因此,我將這裏的'f'移到了函數中:'return do_something(f);'。 – GManNickG

+0

@G ManNickG:我沒有時間去看待這些標準或警告,因此只能作出評論而不是回答。我認爲你是對的。 : - ] – ildjarn

回答

4

是。見[class。複製] p32

當滿足或將滿足複製操作的條件時,除了源對象是函數參數,要複製的對象由左值指定,過載首先執行選擇複製構造函數的分辨率,就好像對象是由右值指定的一樣。如果重載解析失敗,或者如果所選構造函數的第一個參數的類型不是對對象的類型(可能爲cv-qualified)的右值引用,則將該對象視爲左值,重新執行重載解析。 [注意:無論是否發生複製刪除,都必須執行這種兩階段重載解析。如果不執行elision,它將確定要調用的構造函數,即使該函數沒有被使用,所選的構造函數也必須是可訪問的。 - 尾註]

+0

+1爲必要的標準引文。 – ildjarn

2

在這種情況下,由於返回值有一個名稱(f),這將是NRVO(命名返回值優化)將適用這一點。

因此,僅基於文字的技術答案,就是缺乏RVO不會阻止複製省略,因爲NRVO仍然可以允許它。

過去,我相信移動之間的選擇/返回值的副本可以/將依賴於foo的定義 - 有一定的時間它會被複制,而不是移動,例如,如果你已經明確刪除移動構造函數並移動賦值運算符,或者您尚未定義移動構造/賦值,並且不適合隱式合成它們。

編輯:[回覆編輯的問題]:有一個移動構造函數仍然不能保證結果將被移動。一個明顯的例子是,如果你刪除了移動賦值操作符,並且正在分配結果(而不是用它來初始化)。在這種情況下,刪除的移動賦值操作符將阻止移動返回值。

回答您可能已經在,不過,一般的規則是,將移動如果可能的話做了得到什麼,並且當且僅當一些防止被移動的結果,它會回落到複製。

+0

我想我沒有在問題中清楚地表達我的意圖。我只想知道我可以在最壞的情況下將對象移動而不是複製。 RVO或NRVO都會發生,我很高興。我也應該說移動構造函數和移動賦值不會被刪除。謝謝。 – haotang

+3

@Jerry:該​​代碼中有兩個可能的副本。第一個是從局部變量到return語句,通常稱爲(N)RVO。如果存在移動構造函數,那麼保證不復制,編譯器可以應用(N)RVO或不適用,但是如果它不*它必須移動構造。第二個可能的副本位於調用者方,來自返回的對象,並且不屬於問題範圍,因爲它取決於調用者代碼,而不取決於函數。 –

1

的規則是,只要複製省略是允許的,但不會發生,此舉構造函數將被使用,如果它是可用,否則拷貝構造函數將被使用。

確切行爲由[class.copy]/32定義:

當的複製操作的省音的標準被滿足或一個事實,即所述源對象是一個功能參數,以及對象到將被滿足節省被複制是由一個左值指定的,重載解析選擇複製的構造函數首先執行,就好像該對象是由右值指定的一樣。如果重載解析失敗,或者如果所選構造函數的第一個參數的類型不是對對象類型的右值引用(可能是cv-qualified),則將該對象視爲左值,重新執行重載解析。