C C 程式設計 右值引用

2021-10-22 20:01:41 字數 4545 閱讀 2610

右值引用不過是c++的一種新語法,重要的是基於右值引用引申處理的兩種c++程式設計技巧:移動語義和完美**

c++98/03標準中就有引用,用&表示。但是此種引用方式有乙個缺陷,即正常情況下只能操作c++中的左值,無法對右值新增引用。舉個例子:

int num =10;

int&b = num;

//正確

int&c =10;

//錯誤

如上所示,編輯器允許我們為num左值建立乙個引用,但是不可以為10這個右值建立引用。因此,c++93/03標準中的引用又叫做「左值引用

注意,雖然c++98/03標準不支援右值建立非常量左值引用,但是允許常量左值引用操作右值。也就是說,常量左值引用既可以操作左值,也可以操作右值。例如:

int num =10;

const

int&b = num;

const

int&c =

10;

我們知道,右值往往是沒有名稱的,因此要使用它只能借助引用的方式。這就產生乙個問題,實際開發中我們可能需要對右值進行修改(實現移動語義時就需要),顯然左值引用的方式是行不通的

為此,c++11標準引入了另一種引用方式,稱為右值引用,用&&表示

c++標準委員會在選定右值引用符號時,既希望能選用現有 c++ 內部已有的符號,還不能與 c++ 98 /03 標準產生衝突,因此最終選定了 2 個 『&』 表示右值引用。

需要注意的是,和宣告左值引用一樣,右值引用也必須立即進行初始化操作,而且只能使用右值進行初始化,比如:

int num =10;

//int && a = num; //右值引用不能初始化為左值

int&& a =

10;

和常量左值引用不同的是,右值引用還可以對右值進行修改。比如:

int

&& a =10;

a =100

;cout << a << endl;

程式輸出結果為 100。

另外值得一提的是,c++ 語法上是支援定義常量右值引用的,例如:

const

int&& a =10;

//編譯器不會報錯

但這種定義出來的右值引用並無實際用處。

一方面,右值引用主要用於移動語義和完美**,其中前者需要有修改右值的許可權;其次,常量右值引用的作用就是引用乙個不可修改的右值,這項工作完全可以交給常量左值引用完成。

引入右值引用的原因除了我們可能需要對右值進行修改,還有另外乙個原因,我們來看乙個例子:

std::vector<

int>

foo();

return temp;

}std::vector<

int> v =

foo(

);

在這樣的**中,就傳統的理解而言,函式foo的返回值temp在內部建立然後被賦值給v,然而v獲得這個物件時,會將整個temp拷貝乙份,然後把temp銷毀。如果這個temp非常大,這將造成大量額外的開銷(這也是傳統c++一直被詬病的問題)。在最後一行中,v是左值,foor()返回的值就是右值(也就是純右值),但是,v可以被別的變數俘獲到,而foo()嘗試的那個返回值作為乙個臨時值,一旦被v複製後,將立即被銷毀,無法獲取,也不能修改。而將亡值就定義了這一行為:臨時的值能夠被識別、同時也能夠被移動

從c++11起,編譯器為我們做了一些工作,此處的temp會被進行此隱式右值轉換,等價於static_cast&&>(temp),進而此處的v會將foo區域性返回的值進行移動(移動語義

要拿到乙個將亡值,就需要用到右值引用:t&&,其中t是型別右值引用的宣告讓這個臨時值的生命週期得以延長,只要變數還活著,那麼將**將繼續存活

c++11提供了std::move這個方法將左值引數無條件的轉換為右值,有了它我們就可以方便的獲得乙個右值臨時物件

rv2雖然引用了乙個右值,但由於它是乙個引用,所以rv2依舊是乙個左值

注意,這裡有乙個歷史遺留問題:

int

&a = std::

move(1

);//不合法,非常量左引用無法引用右值

const

int&b = std::

move(1

);// 合法,常量做引用允許引用右值

學到這裡,一些讀者可能無法記清楚左值引用和右值引用各自可以引用左值還是右值,這裡給大家一張**,方便大家記憶:

其實,c++11 標準中對右值做了更細緻的劃分,分別稱為純右值(pure value,簡稱 pvalue)和將亡值(expiring value,簡稱 xvalue )。其中純右值就是 c++98/03 標準中的右值(本節中已經做了大篇幅的講解),而將亡值則指的是和右值引用相關的表示式(比如某函式返回的 t && 型別的表示式)。對於純右值和將亡值,都屬於右值,讀者知道即可,不必深究。

在c++語言中,引用是作為一種高效、安全的傳遞資料的方式而存在的。除了一般的引用型別,還可以宣告const引用

我們有以下乙個image類。

intgetsize()

virtual

~image()

}private

:int width =0;

int height =0;

char

* data =

nullptr

;}

上面只是這個類的雛形,只有析構函式、建構函式和取得資料大小的功能。

接下來編寫比較兩個image是否相同的函式。最簡單的形式大致如下:

bool

issame

(image& img)

else

}

這裡使用引用型別的引數,避免了沒有必要的拷貝動作。當然我們還可以做的更好:由於比較函式沒有必要也不應該對比較物件的內容進行修改,所以還可以用下面的形式進行承諾:

bool

issame

(const image& img)

else

}

通過在引數前面增加const修飾符,向issame方法的呼叫者保證,不會修改img的內容

繼續新增將乙個image的一部分merge到另乙個image上的方法。函式的內容大致如下(這裡忽略處理的細節):

void

merge

(image& img)

類似的操作在處理在輸入物件時一般有兩種處理方式。有時希望只是參照而不破壞輸入資料,這時可以使用前面講到的為引數增加const修飾符的方式來承諾;有時為了提高效率或者其他的原因希望可以接管輸入的資料,就像上面**的狀態。這時的行為更像是資料移動。

對於第二種方式,如果僅僅定義一般的引用型別,利用者根本沒有辦法宣告來確定這個操作是否會接管引數中的資料,這種不確定性會造成恨到的麻煩。

這個時候就可以使用右值引用

void

merge

(image&& img)

我們將引數宣告為右值引用,要求像乙個臨時變數一樣的使用資料。使用這個函式的方法如下:

image img1

(100

,100);

image img2

(100

,200);

img1.

merge

(std::

move

(img2)

);

注意**中的std::move,這是標準庫中提供的方法,它可以將左值顯式轉換為右值引用型別,從而告訴編譯器,可以向右值(臨時變數)一樣處理它。時也意味著接下來除了對img2賦值或銷毀以外,不再使用它。

c++11通過使用右值引用提供了一種接管資料的方法

如果說使用const修飾符可以對外承諾不對引數進行修改的話,那麼使用右值引用就是對外要求接管引數資料的權力

C C 左值 右值及引用

目錄 c和c 中定義了引用型別 reference type 存在左值引用 lvalue reference 而在c 11中,新增了右值引用 rvalue reference 這一概念,雖然個人感覺右值引用用處不大,但在此一併討論。首先,我們討論左值和右值兩個概念。左值 lvalue 乙個標識非臨時...

C C 筆記 之左值引用和右值引用

1 左值和右值的概念 左值是可以放在賦值號左邊可以被賦值的值 左值必須要在記憶體中有實體 右值當在賦值號右邊取出值賦給其他變數的值 右值可以在記憶體也可以在cpu暫存器。乙個物件被用作右值時,使用的是它的內容 值 被當作左值時,使用的是它的位址。2 引用 引用是c 語法做的優化,引用的本質還是靠指標...

左值 右值 左值引用 右值引用

2015 06 01 15 07 404人閱讀收藏 舉報 c 11 5 一 c 中的左值和右值 誤區 左值位於等號左邊,右值位於等號右邊。c 11中的定義 左值表示式表示的是乙個物件的身份 在記憶體中的位置 而右值表示式表示的是物件的值 內容 左值和右值都是針對表示式而言的,左值是持久的,右值是短暫...