C++左值與右值,右值引用,移動語義與完美轉(zhuǎn)發(fā)詳解_第1頁
C++左值與右值,右值引用,移動語義與完美轉(zhuǎn)發(fā)詳解_第2頁
C++左值與右值,右值引用,移動語義與完美轉(zhuǎn)發(fā)詳解_第3頁
C++左值與右值,右值引用,移動語義與完美轉(zhuǎn)發(fā)詳解_第4頁
C++左值與右值,右值引用,移動語義與完美轉(zhuǎn)發(fā)詳解_第5頁
已閱讀5頁,還剩4頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

第C++左值與右值,右值引用,移動語義與完美轉(zhuǎn)發(fā)詳解目錄C++左值與右值、右值引用、移動語義與完美轉(zhuǎn)發(fā)一、左值和右值的定義二、如何判斷一個表達式是左值還是右值(大多數(shù)場景)三、C++右值引用四、std::move()與移動語義五、完美轉(zhuǎn)發(fā)總結(jié)

C++左值與右值、右值引用、移動語義與完美轉(zhuǎn)發(fā)

在C++或者C語言中,一個表達式(可以是字面量、變量、對象、函數(shù)的返回值等)根據(jù)其使用場景不同,分為左值表達式和右值表達式。

一、左值和右值的定義

1.左值的英文為locatorvalue,簡寫為lvalue,可意為存儲在內(nèi)存中、有明確存儲地址(可尋址)的數(shù)據(jù)

2.右值的英文為readvalue,簡寫為rvalue,指的是那些可以提供數(shù)據(jù)值的數(shù)據(jù)(不一定可尋址,例如存儲與寄存器中的數(shù)據(jù))

二、如何判斷一個表達式是左值還是右值(大多數(shù)場景)

1.可位于賦值號(=)左側(cè)的表達式就是左值;反之,只能位于賦值號右側(cè)的表達式就是右值。例如:

inta=5;

其中,變量a就是一個左值,而字面量5就是一個右值。

注:C++中的左值可以當(dāng)作右值使用,反之則不行,如

intb=10;//b是一個左值

a=b;//a、b都是左值,只不過b可以當(dāng)作右值使用

10=b;//錯誤,10是一個右值,不能當(dāng)作左值使用

2.有名稱的、可以獲取到存儲地址的表達式即為左值;反之則為右值

以上面定義的變量a、b為例,a和b是變量名,則通過a和b可以獲得他們的存儲地址,因此a和b都是左值;反之,字面量5、10,它們既沒有名稱,也無法獲取其存儲地址(字面量通常存儲在寄存器中,或者和代碼存儲在一起),因此5、10都是右值

三、C++右值引用

C++98/03標(biāo)準(zhǔn)中就有引用,但這里的引用只能操作左值,無法對右值添加引用,故被稱為左值引用,例如:

intnum=10;

intb=num;//正確

intc=10;//錯誤

注意,雖然C++98/03標(biāo)準(zhǔn)不支持為右值建立非常量左值引用,但允許使用常量左值引用操作右值。即,常量左值引用既可以操作左值,也可以操作右值,例如:

intnum=10;

constintb=num;//正確

constintc=10;//正確

為什么需要右值引用?

右值往往是沒有名稱的,因此要使用它只能借助引用的方式。這就產(chǎn)生一個問題,實際開發(fā)中我們可能需要對右值進行修改(如實現(xiàn)移動語義時),而是用常量左值引用的方式是無法做到這一點的。

為此,C++11新標(biāo)準(zhǔn)引入了另一種引用方式,成為右值引用,用表示

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

intnum=10;

inta=num;//錯誤,右值引用不能初始化為左值

inta=10;//正確

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

inta=10;

a=100;//正確

另外,C++語法上是支持定義常量右值引用的,例如:

constinta=10;

但這種定義出來的右值引用并無實際用處。一方面,右值引用主要用于移動語義和完美轉(zhuǎn)發(fā),其中前者需要有修改右值的權(quán)限;其次,常量右值引用的作用就是引用一個不可修改的右值,而這項工作常量左值引用就可以完成

四、std::move()與移動語義

C++11標(biāo)準(zhǔn)中,借助右值引用可以為指定類添加移動構(gòu)造函數(shù),這樣當(dāng)使用該類的右值對象(可以理解為臨時對象)初始化同類對象時,編譯器會優(yōu)先選擇移動構(gòu)造函數(shù)。

注:移動構(gòu)造函數(shù)的調(diào)用時機是:用同類的右值對象初始化新對象。那么當(dāng)用此類的左值對象(有名稱,能獲取其存儲地址的實例對象)初始化同類對象時,如何調(diào)用移動構(gòu)造函數(shù)呢?C++11給出的解決方案就是調(diào)用std::move()函數(shù)。

雖然move是移動的意思,但是該函數(shù)并不移動任何數(shù)據(jù),它的功能是將某個左值強制轉(zhuǎn)化為右值,常用于實現(xiàn)移動語義

用法示例:

#includeiostream

#includeutility

usingnamespacestd;

classmovedemo

public:

movedemo():num(newint(0)){

cout"construct!"endl;

//copyconstructor

movedemo(constmovedemod):num(newint(*d.num)){

cout"copyconstrct!"endl;

//moveconstructor

movedemo(movedemod):num(d.num){

d.num=NULL;

cout"moveconstruct!"endl;

private:

int*num;

intmain()

movedemodemo;

cout"demo2:\n";

movedemodemo2=demo;

cout"demo3:\n";

movedemodemo3=std::move(demo);//執(zhí)行完之后demo.num會置為空

return0;

程序運行結(jié)果:

construct!demo2:copyconstrct!demo3:moveconstruct!

通過觀察程序的輸出結(jié)果,以及對比demo2和demo3初始化操作不難得知,demo對象作為左值,直接用于初始化demo2對象,其底層調(diào)用的是拷貝構(gòu)造函數(shù);而通過調(diào)用move()函數(shù)可以得到demo對象的右值形式,用其初始化demo3對象,編譯器會優(yōu)先調(diào)用移動構(gòu)造函數(shù)。

五、完美轉(zhuǎn)發(fā)

什么是完美轉(zhuǎn)發(fā)?

它指的是函數(shù)模板可以將自己的參數(shù)完美地轉(zhuǎn)發(fā)給內(nèi)部調(diào)用的其他函數(shù)。所謂完美,即不僅能準(zhǔn)確地轉(zhuǎn)發(fā)參數(shù)的值,還能保證被轉(zhuǎn)發(fā)參數(shù)的左、右值屬性不變

舉個栗子:

templatetypenameTvoidfunction(Tt){otherdef(t);}

如上所示,function()函數(shù)模板中調(diào)用了otherdef()函數(shù)。在此基礎(chǔ)上,完美轉(zhuǎn)發(fā)指的是:如果function()函數(shù)接收到的參數(shù)t為左值,那么該函數(shù)傳遞給otherdef()的參數(shù)t也是左值;反之如果function()函數(shù)接收到的參數(shù)t為右值,那么傳遞給otherdef()函數(shù)的參數(shù)t也必須為右值。

顯然,上面這個例子中,function()函數(shù)模板并沒有實現(xiàn)完美轉(zhuǎn)發(fā)。一方面,參數(shù)t為非引用類型,這意味著在調(diào)用function()函數(shù)時,實參將值傳遞給形參的過程就需要額外進行一次拷貝操作;另一方面,無論調(diào)用function()函數(shù)模板時傳遞給參數(shù)t的是左值還是右值,對于函數(shù)內(nèi)部的參數(shù)t來說,它有自己的名稱,也可以獲取它的存儲地址,因此它永遠都是左值,即傳遞給otherdef()函數(shù)的參數(shù)t永遠都是左值??傊?,無論從哪個角度看,function()函數(shù)的定義都不完美。

為什么需要完美轉(zhuǎn)發(fā)?

C++11新標(biāo)準(zhǔn)中引入了右值引用和移動語義,因此很多場景中是否實現(xiàn)完美轉(zhuǎn)發(fā),直接決定了該參數(shù)的傳遞過程使用的是拷貝語義(調(diào)用拷貝構(gòu)造函數(shù))還是移動語義(調(diào)用移動構(gòu)造函數(shù))

如何實現(xiàn)完美轉(zhuǎn)發(fā)?

C++98/03標(biāo)準(zhǔn):由于沒有右值引用,只能通過重載函數(shù)模板(可通過常量左值引用傳遞右值)的方式實現(xiàn)轉(zhuǎn)發(fā),且這種方式存在弊端,如:此實現(xiàn)方式只適用于模板函數(shù)僅有少量參數(shù)的情況,否則就需要編寫大量的重載函數(shù)模板,造成代碼的冗余。

為了方便用戶更快速地實現(xiàn)完美轉(zhuǎn)發(fā),C++11標(biāo)準(zhǔn)中允許在函數(shù)模板中使用右值引用來實現(xiàn)完美轉(zhuǎn)發(fā)

C++11標(biāo)準(zhǔn)中規(guī)定,通常情況下右值引用形式的參數(shù)只能接收右值,不能接收左值。但對于函數(shù)模板中使用右值引用語法定義的參數(shù)來說,它不再遵守這一規(guī)定,既可以接收右值,也可以接收左值(此時的右值引用又被稱為萬能引用)

仍以function()函數(shù)為例,在C++11標(biāo)準(zhǔn)中實現(xiàn)完美轉(zhuǎn)發(fā),只需編寫如下一個模板函數(shù)即可:

templatetypenameTvoidfunction(Tt){otherdef(t);}

此模板函數(shù)的參數(shù)t既可以接收左值,也可以接收右值。但僅僅使用右值引用作為函數(shù)模板的參數(shù)是遠遠不夠的,還有一個問題需要解決,即如果調(diào)用function()函數(shù)時為其傳遞一個左值引用或右值引用的實參,如下所示:

intn=10;

intnum=n;

function(num);//T為intintnum2=11;function(num2);//T為int

其中由function(num)實例化的函數(shù)底層就變成了function(intt),而由function(num2)實例化的函數(shù)底層則變成了function(intt)。C++98是不支持這種語法的,而C++11為了更好地實現(xiàn)完美轉(zhuǎn)發(fā),專門為其指定了新的類型匹配規(guī)則,又稱為引用折疊規(guī)則(假設(shè)A表示實際傳遞參數(shù)的類型):

當(dāng)實參為左值或左值引用(A)時,函數(shù)模板中T將轉(zhuǎn)變?yōu)锳(A=A);當(dāng)實參為右值或右值引用(A)時,函數(shù)模板中T將轉(zhuǎn)變?yōu)锳(A=A);

上述規(guī)則的含義是:在實現(xiàn)完美轉(zhuǎn)發(fā)時,只要函數(shù)模板的參數(shù)類型為T,則C++可以自行準(zhǔn)確地判定實際傳入的實參是左值還是右值

通過將函數(shù)模板的形參類型設(shè)置為T,我們可以很好地解決接收左、右值的問題。但除此之外,還需要解決一個問題,即無論傳入的形參是左值還是右值,對于函數(shù)模板內(nèi)部來說,形參既有名稱又能尋址,因此它都是左值。那么如何才能將函數(shù)模板接收到的形參連同其左、右值屬性,一起傳遞給被調(diào)用的函數(shù)呢?

答案就是使用C++11提供的模板函數(shù)forward(),注意其和move的區(qū)別是forward要通過顯式模板實參來使用

用法示例:

#includeiostream

#includeutilityusingnamespacestd;//重載被調(diào)用函數(shù),查看完美轉(zhuǎn)發(fā)的效果

templatetypenameT

voidprint(Tt)

cout"lvalue"endl;

templatetypenameT

voidprint(Tt)

{cout"rvalue"endl;

templatetypenameT

voidTestForward(Tv)

print(v);

print(std::forwardT(v));

print(std::move(v)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論