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

詳解C++中的左值,純右值和將亡值

 更新時(shí)間:2022年09月27日 08:40:32   作者:Mi?ronin  
C++中本身是存在左值,右值的概念,但是在C11中又出現(xiàn)了左值,純右值,將亡值得概念;這里我們主要介紹這些值的概念,感興趣的可以了解一下

引入

C++中本身是存在左值,右值的概念,但是在C11中又出現(xiàn)了左值,純右值,將亡值得概念;這里我們主要介紹這些值的概念。

一.表達(dá)式

定義:由運(yùn)算符和運(yùn)算對象構(gòu)成的計(jì)算式(類似數(shù)學(xué)中的算術(shù)表達(dá)式)

每個(gè) C++ 表達(dá)式(帶有操作數(shù)的操作符、字面量、變量名等)可按照兩種獨(dú)立的特性加以辨別:**類型和值類別 **(value category)。每個(gè)表達(dá)式都具有某種非引用類型,且每個(gè)表達(dá)式只屬于三種基本值類別中的一種:純右值 (prvalue)、亡值 (xvalue)、左值 (lvalue)。

二.值類別

對于表達(dá)式來說:表達(dá)式是可以求值的,對表達(dá)式求值將得到一個(gè)結(jié)果,這個(gè)結(jié)果有兩個(gè)屬性:類型和值類別。

在C++11以后表達(dá)式按值類別分類,必然屬于以下三者之一:

  • 左值
  • 將亡值
  • 純右值

其中,左值和將亡值合稱為泛左值,純右值和將亡值合稱為右值

三.左值

左值:能夠用&取地址的表達(dá)式為左值表達(dá)式

下列表達(dá)式是左值表達(dá)式:

  1. 變量、函數(shù)、模板形參對象 (C++20 起)或數(shù)據(jù)成員的名字,不論類型,例如 std::cin 或 std::endl。即使變量的類型是右值引用,由它的名字構(gòu)成的表達(dá)式仍是左值表達(dá)式;
  2. 返回類型是左值引用的函數(shù)調(diào)用或重載運(yùn)算符表達(dá)式,例如 std::getline(std::cin, str)、std::cout << 1、str1 = str2 或 ++it;
  3. a = b,a += b,a %= b,以及所有其他內(nèi)建的賦值及復(fù)合賦值表達(dá)式;
  4. ++a 和 --a,內(nèi)建的前置自增與前置自減表達(dá)式;
  5. *p,內(nèi)建的間接尋址表達(dá)式;
  6. a[n] 和 n[a],內(nèi)建的下標(biāo)表達(dá)式,當(dāng) a[n] 中的一個(gè)操作數(shù)是數(shù)組左值時(shí) (C++11 起);
  7. a.m,對象成員表達(dá)式,除了 m 是成員枚舉項(xiàng)或非靜態(tài)成員函數(shù),或者 a 是右值而 m 是對象類型的非靜態(tài)數(shù)據(jù)成員的情況;
  8. p->m,內(nèi)建的指針成員表達(dá)式,除了 m 是成員枚舉項(xiàng)或非靜態(tài)成員函數(shù)的情況;
  9. a.*mp,對象的成員指針表達(dá)式,其中 a 是左值且 mp 是數(shù)據(jù)成員指針;
  10. p->*mp,內(nèi)建的指針的成員指針表達(dá)式,其中 mp 是數(shù)據(jù)成員指針;
  11. a, b,內(nèi)建的逗號表達(dá)式,其中 b 是左值;
  12. a ? b : c,對某些 b 和 c 的三元條件表達(dá)式(例如,當(dāng)它們都是同類型左值時(shí),但細(xì)節(jié)見定義);
  13. 字符串字面量,例如 “Hello, world!”;
  14. 轉(zhuǎn)換到左值引用類型的轉(zhuǎn)型表達(dá)式,例如 static_cast<int&>(x);
  15. 返回類型是到函數(shù)的右值引用的函數(shù)調(diào)用表達(dá)式或重載的運(yùn)算符表達(dá)式;(C++11 起)
  16. 轉(zhuǎn)換到函數(shù)的右值引用類型的轉(zhuǎn)型表達(dá)式,如 static_cast<void (&&)(int)>(x)。(C++11 起)

性質(zhì):

  1. 可以通過內(nèi)建的取址運(yùn)算符取左值的地址:&++i[1] 及 &std::endl 是合法表達(dá)式。
  2. 可修改的左值可用作內(nèi)建賦值和內(nèi)建復(fù)合賦值運(yùn)算符的左操作數(shù)。
  3. 左值可以用來初始化左值引用;這會將一個(gè)新名字關(guān)聯(lián)給該表達(dá)式所標(biāo)識的對象。

四.純右值

滿足下列條件之一:

1.本身就是純粹的字面值,如3,false,12.13

2.求值結(jié)果相當(dāng)于字面值或是一個(gè)不具名的臨時(shí)對象

下列表達(dá)式是純右值表達(dá)式:

  1. (除了字符串字面量之外的)字面量,例如 42、true 或 nullptr;
  2. 返回類型是非引用的函數(shù)調(diào)用或重載運(yùn)算符表達(dá)式,例如 str.substr(1, 2)、str1 + str2 或 it++;
  3. a++ 和 a–,內(nèi)建的后置自增與后置自減表達(dá)式;
  4. a + b、a % b、a & b、a << b,以及其他所有內(nèi)建的算術(shù)表達(dá)式;
  5. a && b、a || b、!a,內(nèi)建的邏輯表達(dá)式;
  6. a < b、a == b、a >= b 以及其他所有內(nèi)建的比較表達(dá)式;
  7. &a,內(nèi)建的取地址表達(dá)式;
  8. a.m,對象成員表達(dá)式,其中 m 是成員枚舉項(xiàng)或非靜態(tài)成員函數(shù)[2],或其中 a 是右值且 m 是非- - 引用類型的非靜態(tài)數(shù)據(jù)成員 (C++11 前);
  9. p->m,內(nèi)建的指針成員表達(dá)式,其中 m 是成員枚舉項(xiàng)或非靜態(tài)成員函數(shù)[2];
  10. a.*mp,對象的成員指針表達(dá)式,其中 mp 是成員函數(shù)指針[2],或其中 a 是右值且 mp 是數(shù)據(jù)成員指針 (C++11 前);
  11. p->*mp,內(nèi)建的指針的成員指針表達(dá)式,其中 mp 是成員函數(shù)指針[2];
  12. a, b,內(nèi)建的逗號表達(dá)式,其中 b 是右值;
  13. a ? b : c,對某些 b 和 c 的三元條件表達(dá)式(細(xì)節(jié)見定義);
  14. 轉(zhuǎn)換到非引用類型的轉(zhuǎn)型表達(dá)式,例如 static_cast(x)、std::string{} 或 (int)42;
  15. this 指針;
  16. 枚舉項(xiàng);
  17. 非類型模板形參,除非它的類型是類或 (C++20 起)左值引用類型;
  18. lambda 表達(dá)式,例如 [](int x){ return x * x; };(C++11 起)
  19. requires 表達(dá)式,例如 requires (T i) { typename T::type; };(C++20 起)
  20. 概念的特化,例如 std::equality_comparable (C++20 起)

性質(zhì):

純右值不具有多態(tài):它所標(biāo)識的對象的動(dòng)態(tài)類型始終是該表達(dá)式的類型。

非類非數(shù)組的純右值不能有 cv 限定,除非它被實(shí)質(zhì)化以綁定到 cv 限定類型的引用 (C++17 起)。(注意:函數(shù)調(diào)用或轉(zhuǎn)型表達(dá)式可能生成非類的 cv 限定類型的純右值,但它的 cv 限定符通常被立即剝除。)

純右值不能具有不完整類型(除了類型 void(見下文),或在 decltype 說明符中使用之外)

純右值不能具有抽象類類型或它的數(shù)組類型。

易混:

++i是左值,i++是右值

前者,對i加1后再賦給i,最終的返回值就是i,所以,++i的結(jié)果是具名的,名字就是i;而對于i++而言,是先對i進(jìn)行一次拷貝,將得到的副本作為返回結(jié)果,然后再對i加1,由于i++的結(jié)果是對i加1前i的一份拷貝,所以它是不具名的。

假設(shè)自增前i的值是6,那么,++i得到的結(jié)果是7,這個(gè)7有個(gè)名字,就是i;而i++得到的結(jié)果是6,這個(gè)6是i加1前的一個(gè)副本,它沒有名字,i不是它的名字,i的值此時(shí)也是7。可見,++i和i++都達(dá)到了使i加1的目的,但兩個(gè)表達(dá)式的結(jié)果不同。

解引用表達(dá)式 * p是左值,取地址表達(dá)式 &a 是純右值。

&(*p) 一定是正確的,因?yàn)?*p得到的是p指向的實(shí)體,&( *p)得到的就是這一實(shí)體的地址,正是p的值。由于 &(*p)的正確,所以 *p是左值。而對&a而言,得到的是a的地址,相當(dāng)于unsigned int型的字面值,所以是純右值。

a+b、a&&b、ab 都是純右值

a+b得到的是不具名的臨時(shí)對象,而 a&&b 和 ab 的結(jié)果非 true 即 false,相當(dāng)于字面值。

五.將亡值

在C++11之前的右值和C++11中的純右值是等價(jià)的。C++11中的將亡值是隨著右值引用的引入而新引入的。換言之,“將亡值”概念的產(chǎn)生,是由右值引用的產(chǎn)生而引起的,將亡值與右值引用息息相關(guān)。所謂的將亡值表達(dá)式,就是下列表達(dá)式:

  • 返回右值引用的函數(shù)的調(diào)用表達(dá)式
  • 轉(zhuǎn)換為右值引用的轉(zhuǎn)換函數(shù)的調(diào)用表達(dá)式

在C++11中,我們用左值去初始化一個(gè)對象或?yàn)橐粋€(gè)已有對象賦值時(shí),會調(diào)用拷貝構(gòu)造函數(shù)或拷貝賦值運(yùn)算符來拷貝資源(所謂資源,就是指new出來的東西),而當(dāng)我們用一個(gè)右值(包括純右值和將亡值)來初始化或賦值時(shí),會調(diào)用移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符來移動(dòng)資源,從而避免拷貝,提高效率。當(dāng)該右值完成初始化或賦值的任務(wù)時(shí),它的資源已經(jīng)移動(dòng)給了被初始化者或被賦值者,同時(shí)該右值也將會馬上被銷毀(析構(gòu))。

也就是說,當(dāng)一個(gè)右值準(zhǔn)備完成初始化或賦值任務(wù)時(shí),它已經(jīng)“將亡”了。而上面1)和2)兩種表達(dá)式的結(jié)果都是不具名的右值引用,它們屬于右值。

又因?yàn)?/p>

1)這種右值是與C++11新生事物——“右值引用”相關(guān)的“新右值”

2)這種右值常用來完成移動(dòng)構(gòu)造或移動(dòng)賦值的特殊任務(wù),扮演著“將亡”的角色,所以C++11給這類右值起了一個(gè)新的名字——將亡值。

下列表達(dá)式是將亡值表達(dá)式:

  1. 返回類型為對象的右值引用的函數(shù)調(diào)用或重載運(yùn)算符表達(dá)式,例如 std::move(x);
  2. a[n],內(nèi)建的下標(biāo)表達(dá)式,它的操作數(shù)之一是數(shù)組右值;
  3. a.m,對象成員表達(dá)式,其中 a 是右值且 m 是非引用類型的非靜態(tài)數(shù)據(jù)成員;
  4. a.*mp,對象的成員指針表達(dá)式,其中 a 是右值且 mp 是數(shù)據(jù)成員指針;
  5. a ? b : c,對某些 b 和 c 的三元條件表達(dá)式(細(xì)節(jié)見定義);
  6. 轉(zhuǎn)換到對象的右值引用類型的轉(zhuǎn)型表達(dá)式,例如 static_cast<char&&>(x);
  7. 在臨時(shí)量實(shí)質(zhì)化后,任何指代該臨時(shí)對象的表達(dá)式。(C++17 起)

性質(zhì):

1.與右值相同。

2.與泛左值相同。

特別是,與所有的右值類似,亡值可以綁定到右值引用上,而且與所有的泛左值類似,亡值可以是多態(tài)的,而且非類的亡值可以有 cv 限定。

六.注意

1)字符串字面值是左值。

不是所有的字面值都是純右值,字符串字面值是唯一例外。

早期C++將字符串字面值實(shí)現(xiàn)為char型數(shù)組,實(shí)實(shí)在在地為每個(gè)字符都分配了空間并且允許程序員對其進(jìn)行操作,

cout<<&("abc")<<endl;
const char *p_char="abc";//注意不是char *p_char=&("abc");

這樣的代碼都是可以編譯通過的。

2)具名的右值引用是左值,不具名的右值引用是右值。

void foo(X&& x) 
{ 
    X anotherX = x; //后面還可以訪問x
}

上面X是自設(shè)計(jì)的類型,并且,其有一個(gè)指針成員p指向了在堆中分配的內(nèi)存;參數(shù)x是X的右值引用。如果將x視為右值,那么,X another X = x;一句將調(diào)用X類的移動(dòng)構(gòu)造函數(shù),而我們知道,這個(gè)移動(dòng)構(gòu)造函數(shù)的主要工作就是將x的p指針的值賦給anotherX的p指針,然后將x的p指針置為nullptr。而在后面,我們還可以訪問x,也就是可以訪問x.p,而此時(shí)x.p已經(jīng)變成了nullptr,這就可能發(fā)生意想不到的錯(cuò)誤。

3)注釋

①只有當(dāng)存在兩個(gè)或兩個(gè)以上的運(yùn)算對象時(shí)才需要運(yùn)算符連接,單獨(dú)的運(yùn)算對象也可以是表達(dá)式,例如上面提到的字面值和變量。

②確切說,是表達(dá)式的結(jié)果的值類別,但我們一般不刻意區(qū)分表達(dá)式和表達(dá)式的求值結(jié)果,所以這里稱“表達(dá)式的值類別”。

③當(dāng)我們將函數(shù)名作為一個(gè)值來使用時(shí),該函數(shù)名自動(dòng)轉(zhuǎn)換為指向?qū)?yīng)函數(shù)的指針。

④關(guān)于右值引用本身,沒什么可說的,就是指可以綁定到右值上的引用,用"&&"表示,如int&&rra=6;。相比之下,與右值引用相關(guān)的一些主題,如移動(dòng)語義、引用疊加、完美轉(zhuǎn)發(fā)等,更值得我們深入探討。這些內(nèi)容,在下在后續(xù)文章中都會詳細(xì)介紹。

⑤前提是該右值(如自定義的類X)有移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符可供調(diào)用(有時(shí)候是沒有的,關(guān)于這些知識,后續(xù)文章在講移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符時(shí)會詳述)。

⑥在本文的例二中,如果將get_a_X()的返回值由X的右值引用改為X對象,則get_a_X()是純右值表達(dá)式(如前所述,返回非引用類型的函數(shù)調(diào)用是純右值),此時(shí)Foo(get_a_X());一句調(diào)用的仍然是類X的移動(dòng)構(gòu)造函數(shù),這就是一個(gè)純右值完成移動(dòng)構(gòu)造的例子。

到此這篇關(guān)于詳解C++中的左值,純右值和將亡值的文章就介紹到這了,更多相關(guān)C++值內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論