為了支援移動操作,新標準引入了一種新的引用型別----右值引用(rvalue reference),所謂右值引用就是必須繫結到右值的引用。我們通過&&而不是&來獲得右值引用,右值引用有乙個重要的性質----只能繫結到乙個將要銷毀的物件。因此,我們可以自由地將乙個右值引用的資源「移動」到另乙個物件中。
一般而言,乙個左值表示式表示的是乙個物件的身份,而乙個右值表示式表示的是物件的值,類似任何引用,右值引用也是某個物件的另乙個名字,對於常規引用(左值引用),不能將其繫結到要求轉換的表示式、字面常量或是返回右值的表示式,右值引用有著完全相反的特性,我們可以將乙個右值引用繫結到這類表示式上,但不能將乙個右值引用直接繫結到乙個左值上。
int i = 42;
int& r = i; //正確:r引用i
int&& rr = i; //錯誤:不能將乙個右值引用繫結到乙個左值上
int& r2 = i * 42; //錯誤:i*42是乙個右值
const int& r3 = i * 42; //正確:我們可以將乙個const的引用繫結到乙個右值上
int&& rr2 = i * 42; //正確:將rr2繫結到乘法結果上
返回非引用型別的函式,連同算術、關係、位以及後置遞增/遞減運算子,都生成右值。我們不能將乙個左值引用繫結到這類表示式上,但我們可以將乙個const的左值引用或者乙個右值引用繫結到這類表示式上。
左值持久,右值短暫。左值有持久的狀態,而右值要麼是字面常量,要麼是在表示式求值過程中闖將的臨時物件。
由於右值引用只能繫結到臨時物件,所有它有以下特性:
1.所引用的物件將要被銷毀
2.該物件沒有其他使用者
這倆個特性意味著:使用右值引用的**可以自由地接管所引用的物件的資源。
變數是左值,變數可以看做只有乙個運算物件而沒有運算子的表示式,變數表示式也有左值/右值屬性,變數表示式都是左值,帶來的結果就是,我們不能將乙個右值引用繫結到乙個右值引用型別變數上。
int&& rr1 = 42; //正確:字面常量是右值
int&& rr2 = rr1; //錯誤:表示式rr1是左值
雖然不能將乙個右值引用直接繫結到乙個左值上,但我們可以顯式地將乙個左值轉換為對應的右值引用型別(不推薦),我們還可以通過呼叫乙個名為move的新標準庫函式來獲得繫結到左值上的右值引用。此函式定義在標頭檔案utility中。
int&& rr1 = 42;
int&& rr2 = (int&&)rr1; //不推薦
int&& rr3 = std::move(rr1);
move呼叫告訴編譯器:我們有乙個左值,但我們希望像乙個右值一樣處理它,我們必須認識到,呼叫move就意味著承諾:除了對rr1賦值或銷毀它外,我們將不再使用它。在呼叫move之後,我們不能對移後源物件的值做任何假設(不能使用乙個移後源物件的值)。
標準庫move函式和forward函式都是模版函式,在標準庫的定義中它們都接受乙個右值引用的函式形參,在函式模板中,右值引用形參可以匹配任何型別,如果我們的應用程式也定義了乙個接受單一形參的move函式,則不管該形參是什麼型別,應用程式的move函式都將與標準庫的版本衝突,forward函式也是如此。因此move(以及forward)的名字衝突要比其他標準庫函式的衝突頻繁的多,所以建議使用帶限定語的完整版本。
標準庫move函式是使用右值引用的模板的乙個很好的例子,雖然不能直接將乙個右值引用繫結到乙個左值上,但可以用move獲得乙個繫結到左值上的右值引用,由於move本質上可以接受任何型別的實參,因此我們不會驚訝於它是乙個函式模板。
標準庫是這樣定義move的:
templatetypename remove_reference::type&& move(t&& t)
remove_reference(標準型別轉換模板):remove_reference模板有乙個模板型別引數和乙個名為type的(public)型別成員,如果用乙個引用型別例項化該模板,則type將表示被引用的型別,例如例項化remove_reference,則type成員將是int,remove_reference::type脫去引用,剩下元素型別本身,注意type是乙個類的成員,而該類依賴於乙個模板引數,因此,我們必須在返回型別的宣告中使用typename來告知編譯器,type表示乙個型別。
這段**中move的函式引數t&&是乙個指向模板型別引數的右值引用,通過引用摺疊,此引數可以與任何型別的實參匹配,特別是,我們既可以傳遞給move乙個左值,也可以傳遞給它乙個右值。
string s1("hi!"), s2;
s2 = std::move(string("bye!")); //正確:從乙個右值移動資料
s2 = std::move(s1); //正確:但在賦值之後,s1的值是不確定的
std::move(string("bye!")):
因此這個呼叫例項化move,即函式 string&& move(string&& t),函式體返回static_cast(t),t的型別已經是string&&,因此呼叫結果就是它所接受的右值引用。
std::move(s1):
因此這個呼叫例項化move,即 string&& move(string& t),這正是我們所尋求的----我們希望將乙個右值引用繫結到乙個左值,此情況下t的型別為string&,cast將其轉換為string&&。
通常情況下,static_cast只能用於其他合法的型別轉換,但是,這裡又有一條針對右值引用的特許規則:雖然不能隱式地將乙個左值轉換為右值引用,但我們可以用static_cast顯式地將乙個左值轉換為乙個右值引用。對於操作右值引用的**來說,將乙個右值引用繫結到乙個左值的特性允許它們階段左值,截斷乙個左值是安全的(為什麼安全?),而且通過允許進行這樣的轉換,c++認可這種做法。統一使用std::move使得我們在程式中查詢潛在的截斷左值的**變得很容易。
右值引用 移動建構函式和move
左值和右值判斷 1 可位於賦值號 左側的表示式就是左值 反之,只能位於賦值號右側的表示式就是右值。2 有名稱的 可以獲取到儲存位址的表示式即為左值 反之則是右值。例如 int i 10 10 i 錯誤,10為右值,不能當左值用 int j 20 j i i和j都是左值,但是i可以當右值用 以上面定義...
關於c 的 右值 右值引用 move
第一次接觸c move操作就懵逼了,一直想探個究竟,但是右值以及右值引用思考了好長時間,就是不得要領,今天終於有所收穫,寫下第一篇部落格,一方面為了幫助一些剛入門的朋友,另一方面也是幫助自己今後複習。左值是乙個持久的量,右值是乙個短暫的量。那怎麼算持久怎麼算短暫呢?取決於我的 裡有沒有乙個變數來儲存...
右值引用和move語義
標籤 c 11 c lvalue rvalue 2013 10 04 20 49 2909人閱讀收藏 舉報 c 6 目錄 lvalue 具有儲存性質的物件,即lvalue物件,是指要實際占用記憶體空間 有記憶體位址的那些實體物件,例如 變數 variables 函式 函式指標等。rvalue 相比較...