所以,讓我們說你有:
A compute()
{
A v;
…
return v;
}
而你正在做的:
A a = compute();
有兩種傳輸(複製或移動)中涉及的這個表達。首先,函數中由v
表示的對象必須被轉移到該函數的結果,即由compute()
表達式貢獻的值。我們稱之爲Transfer 1.然後,這個臨時對象被轉移來創建a
表示的對象 - 傳輸2.
在很多情況下,編譯器可以忽略Transfer 1和2 - 直接構造對象v
在a
的位置,並且不需要轉移。在這個例子中,編譯器必須使用Named Return Value Optimization for Transfer 1,因爲返回的對象是被命名的。但是,如果我們禁用複製/移動省略,則每次傳輸都會調用A的拷貝構造函數或其移動構造函數。在大多數現代編譯器中,編譯器將看到v
即將被銷燬,它將首先將其移入返回值。然後這個臨時返回值將被移至a
。如果A
沒有移動構造函數,它將被複制用於兩次傳輸。
現在讓我們看一下:
A compute(A&& v)
{
return v;
}
我們返回的值來自參考被傳遞給函數。編譯器不只是假設v
是一個臨時的,它可以從它移動。在這種情況下,Transfer 1將成爲副本。然後轉移2將是一個舉動 - 沒關係,因爲返回的值仍然是一個臨時的(我們沒有返回一個引用)。但由於我們知道,我們已經採取了對象,我們可以從移動,因爲我們的參數是一個右值引用,我們可以明確地告訴編譯器把v
作爲臨時與std::move
:
A compute(A&& v)
{
return std::move(v);
}
現在轉移1和轉移2將會移動。
之所以編譯器不會自動把v
,定義爲A&&
,作爲右值是安全的一個。弄清楚這不僅僅是太愚蠢。一旦一個對象有一個名字,它可以在整個代碼中被多次引用。試想一下:
A compute(A&& a)
{
doSomething(a);
doSomethingElse(a);
}
如果a
作爲右值是自動處理,doSomething
可以自由地撕裂它的膽量了,這意味着a
傳遞給doSomethingElse
可能無效。即使doSomething
通過值取其參數,該對象也將從下一行中移出,因此無效。爲了避免這個問題,指定的右值引用是左值。這意味着當doSomething
被調用時,a
最壞的情況下將被複制,如果不是僅由左值引用 - 它將在下一行仍然有效。
這是由作者compute
說,「好吧,現在我允許這個值被移出,因爲我確定它是一個臨時對象」。你可以這麼說std::move(a)
。例如,你可以給doSomething
副本,然後讓doSomethingElse
從中移動:
A compute(A&& a)
{
doSomething(a);
doSomethingElse(std::move(a));
}
你有一個鏈接到有問題的文章? –
[相關常見問題](http://stackoverflow.com/questions/3106110/) – fredoverflow
http://cpp-next.com/archive/2009/09/making-your-next-move/,這是一個系列右值引用 – StereoMatching