以一個const
左值參考和服用右值引用是兩回事。
相似之處:
- 亦不會導致複製或移動到發生,因爲他們都是引用。引用只是引用一個對象,它不會以任何方式複製/移動它。
差異:
讓我們來看一些例子:
以const
左值參考:void f(const T& t);
傳遞一個左:
T t; f(t);
這裏,t
是一個左值表達式,因爲它是對象的名稱。 A const
左值引用可以綁定任何內容,因此t
將很樂意通過引用傳遞。沒有任何東西被複制,什麼都不移動
傳遞右值:
f(T());
這裏,T()
是一個右值表達,因爲它產生一個臨時的物體。再次,一個const
左值引用可以綁定到任何東西,所以這沒關係。沒有任何東西被複制,什麼都不移動
在這兩種情況下,該函數內的t
是傳入的對象的引用,它不能由參考被修改是const
。
取右值參考:`void f(T & & t);
傳遞一個左:
T t;
f(t);
這會給你一個編譯器錯誤。右值引用不會綁定到左值。
傳遞右值:
f(T());
這將是很好的,因爲一個rvalue參考可結合一個rvalue。函數內部的參考t
將引用由T()
創建的臨時對象。
現在讓我們考慮std::move
。首先要做的事情是:std::move
實際上並沒有移動任何東西。這個想法是,你給它一個左值,並把它變成一個右值。就是這樣。所以,現在,如果你的f
需要一個右值引用,你可以這樣做:
T t;
f(std::move(t));
這樣做是因爲,雖然t
是一個左值,std::move(t)
是一個右值。現在右值引用可以綁定到它。
那麼,爲什麼你會採取一個右值引用參數?事實上,除了定義移動構造函數和賦值運算符外,您不需要經常這樣做。無論何時定義一個帶右值引用的函數,幾乎肯定會給出一個const
左值引用超載。他們應該幾乎總是成對出現:
void f(const T&);
void f(T&&);
爲什麼這對功能有用?那麼,只要你給它一個左值(或一個const
右值),第一個就會被調用,而第二個會在你給它一個可修改的右值時被調用。接收一個右值通常意味着你已經得到一個臨時對象,這是一個好消息,因爲這意味着你可以摧毀它的內部並根據事實知道它不會存在很久。
因此擁有這對功能可以讓您在知道自己獲得臨時對象時進行優化。
這對函數有一個非常常見的例子:複製和移動構造函數。他們通常被定義就像這樣:
T::T(const T&); // Copy constructor
T::T(T&&); // Move constructor
所以此舉構造是真的只是正在接收一個臨時對象時優化的拷貝構造函數。
當然,被傳遞的對象不是總是是一個臨時對象。如上所示,您可以使用std::move
將左值變爲右值。然後它出現是該函數的臨時對象。使用std::move
基本上說「我允許你把這個對象當作臨時對象。」它是否實際上被移動或不移動是無關緊要的。
但是,除了編寫複製構造函數和移動構造函數之外,最好有一個很好的理由來使用這對函數。如果你正在編寫一個接受對象的函數,並且它的行爲與它是完全一樣的,不管它是否是一個臨時對象,只需通過值來獲取該對象!考慮:
void f(T t);
T t;
f(t);
f(T());
在第一次調用f
時,我們傳遞了一個左值。那將複製到進入函數。在第二次調用f
時,我們傳遞了一個右值。該對象將轉移到的功能中。請參閱 - 我們甚至不需要使用右值引用來使對象有效地移動。我們只是看重它!爲什麼?因爲用於進行復制/移動的構造函數是根據表達式是左值還是右值來選擇的。讓複製/移動構造函數完成他們的工作。
對於不同的參數類型是否產生了相同的代碼 - 嗯,這是一個完全不同的問題。編譯器在下運行,如果規則爲。這只是意味着只要程序按照標準規定行事,編譯器就可以發出任何它喜歡的代碼。因此,如果這些函數碰巧做了完全相同的事情,它們可能會發出相同的代碼。或者他們可能不會。然而,如果你的函數使用一個常量左值引用和右值引用正在做同樣的事情,那麼這是一個不好的跡象。
不會有很多次你應該考慮通過實現移動構造函數時比其他右值引用的論據。而且幾乎總是應該重載一個函數,該函數通過'const'左值引用。 – 2013-03-25 11:59:24
@sftrabbit:請編輯的問題 – 2013-03-25 12:30:46
你不需要的std ::移動(T(T),只是T(T)就足夠了,因爲它是明確的牛逼此功能 – Alon 2013-03-25 12:34:22