欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++左值與右值,右值引用,移動(dòng)語(yǔ)義與完美轉(zhuǎn)發(fā)詳解

 更新時(shí)間:2022年03月24日 10:24:53   作者:Aaron_1997  
這篇文章主要為大家詳細(xì)介紹了Python實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助

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

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

一、左值和右值的定義

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

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

二、如何判斷一個(gè)表達(dá)式是左值還是右值(大多數(shù)場(chǎng)景)

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

int a = 5;

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

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

int b = 10; //b是一個(gè)左值
a = b; //a、b都是左值,只不過(guò)b可以當(dāng)作右值使用
10 = b; //錯(cuò)誤,10是一個(gè)右值,不能當(dāng)作左值使用

2.有名稱(chēng)的、可以獲取到存儲(chǔ)地址的表達(dá)式即為左值;反之則為右值

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

三、C++右值引用

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

int num = 10;
int &b = num; //正確
int &c = 10; //錯(cuò)誤

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

int num = 10;
const int &b = num; //正確
const int &c = 10; //正確

為什么需要右值引用?

右值往往是沒(méi)有名稱(chēng)的,因此要使用它只能借助引用的方式。這就產(chǎn)生一個(gè)問(wèn)題,實(shí)際開(kāi)發(fā)中我們可能需要對(duì)右值進(jìn)行修改(如實(shí)現(xiàn)移動(dòng)語(yǔ)義時(shí)),而是用常量左值引用的方式是無(wú)法做到這一點(diǎn)的。

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

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

int num = 10;
int &&a = num; //錯(cuò)誤,右值引用不能初始化為左值
int &&a = 10; //正確

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

int &&a = 10;
a = 100; //正確

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

const int&& a = 10;

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

四、std::move()與移動(dòng)語(yǔ)義

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

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

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

用法示例:

#include<iostream>
#include<utility>
using namespace std;
class movedemo
{
public:
    movedemo():num(new int(0)) {
        cout << "construct!" << endl;
    }
    //copy constructor
    movedemo(const movedemo &d):num(new int(*d.num)) {
        cout << "copy constrct!" << endl;
    }
    //move constructor
    movedemo(movedemo &&d):num(d.num) {
        d.num = NULL;
        cout << "move construct!" << endl;
    }
private:
    int *num;
};
int main() 
{
    movedemo demo;
    cout << "demo2:\n";
    movedemo demo2 = demo;
    cout << "demo3:\n";
    movedemo demo3 = std::move(demo);//執(zhí)行完之后demo.num會(huì)置為空
    return 0;
}

程序運(yùn)行結(jié)果:

construct!demo2:copy constrct!demo3:move construct!

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

舉個(gè)栗子:

template<typename T>void function(T t) {	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也必須為右值。

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

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

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

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

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

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

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

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

template<typename T>void function(T &&t) {otherdef(t);}

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

int n = 10;
int &num = n;
function(num); //T為int &int &&num2 = 11;function(num2); //T為int &&

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

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

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

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

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

用法示例:

#include <iostream>
#include <utility>using namespace std;//重載被調(diào)用函數(shù),查看完美轉(zhuǎn)發(fā)的效果
template<typename T>
void print(T &t)
{    
cout << "lvalue" << endl;
}
template<typename T>
void print(T &&t) 
{    cout << "rvalue" << endl;
}
template<typename T>
void TestForward(T &&v) 
{    
print(v);    
print(std::forward<T>(v));    
print(std::move(v));
}
int main() 
{    
TestForward(1);  
cout << endl;    
int x = 1;    
TestForward(x);    
cout << endl;    
TestForward(std::forward<int>(x));    
return 0;
}

程序執(zhí)行結(jié)果為:

lvaluervaluervaluelvaluelvaluervaluelvaluervaluervalue

總結(jié)

本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容! 

相關(guān)文章

  • C++11用兩個(gè)線(xiàn)程輪流打印整數(shù)的實(shí)現(xiàn)方法

    C++11用兩個(gè)線(xiàn)程輪流打印整數(shù)的實(shí)現(xiàn)方法

    這篇文章主要介紹了C++11用兩個(gè)線(xiàn)程輪流打印整數(shù)的實(shí)現(xiàn)方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • C++ Opengl旋轉(zhuǎn)功能附源碼下載

    C++ Opengl旋轉(zhuǎn)功能附源碼下載

    這篇文章主要介紹了C++ Opengl旋轉(zhuǎn)功能附源碼下載,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 關(guān)于C++中的static關(guān)鍵字的總結(jié)

    關(guān)于C++中的static關(guān)鍵字的總結(jié)

    C++的static有兩種用法:面向過(guò)程程序設(shè)計(jì)中的static和面向?qū)ο蟪绦蛟O(shè)計(jì)中的static。前者應(yīng)用于普通變量和函數(shù),不涉及類(lèi);后者主要說(shuō)明static在類(lèi)中的作用
    2013-09-09
  • C++實(shí)現(xiàn)簡(jiǎn)易貪吃蛇游戲

    C++實(shí)現(xiàn)簡(jiǎn)易貪吃蛇游戲

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)簡(jiǎn)易貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易掃雷小游戲

    C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易掃雷小游戲

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)簡(jiǎn)易掃雷小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • C語(yǔ)言如何實(shí)現(xiàn)可變參數(shù)詳解

    C語(yǔ)言如何實(shí)現(xiàn)可變參數(shù)詳解

    這種可變參數(shù)可以說(shuō)是C語(yǔ)言一個(gè)比較難理解的部分,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言如何實(shí)現(xiàn)可變參數(shù)的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • c++超細(xì)致講解引用

    c++超細(xì)致講解引用

    引用(reference)就是C++對(duì)C語(yǔ)言的重要擴(kuò)充。引用就是某一變量(目標(biāo))的一個(gè)別名,對(duì)引用的操作與對(duì)變量直接操作完全一樣
    2022-05-05
  • C++實(shí)現(xiàn)點(diǎn)云添加高斯噪聲功能

    C++實(shí)現(xiàn)點(diǎn)云添加高斯噪聲功能

    所謂高斯噪聲是指它的概率密度函數(shù)服從高斯分布(即正態(tài)分布)的一類(lèi)噪聲,這篇文章主要給大家介紹了關(guān)于C++實(shí)現(xiàn)點(diǎn)云添加高斯噪聲功能的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • C語(yǔ)言中的內(nèi)存泄露 怎樣避免與檢測(cè)

    C語(yǔ)言中的內(nèi)存泄露 怎樣避免與檢測(cè)

    堆經(jīng)常會(huì)出現(xiàn)兩種類(lèi)型的問(wèn)題:1.釋放或改寫(xiě)仍在使用的內(nèi)存(稱(chēng)為:“內(nèi)存損壞”)。2.未釋放不再使用的內(nèi)存(稱(chēng)為:“內(nèi)存泄露”)。這是最難被調(diào)試發(fā)現(xiàn)的問(wèn)題之一
    2013-09-09
  • C++實(shí)現(xiàn)動(dòng)態(tài)數(shù)組功能

    C++實(shí)現(xiàn)動(dòng)態(tài)數(shù)組功能

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)動(dòng)態(tài)數(shù)組功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11

最新評(píng)論