右值引用 move函式

2021-10-11 00:22:34 字數 3345 閱讀 7270

為了支援移動操作,新標準引入了一種新的引用型別----右值引用(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 相比較...