網(wǎng)站首頁 編程語言 正文
引入
C++中本身是存在左值,右值的概念,但是在C11中又出現(xiàn)了左值,純右值,將亡值得概念;這里我們主要介紹這些值的概念。
一.表達(dá)式
定義:由運(yùn)算符和運(yùn)算對(duì)象構(gòu)成的計(jì)算式(類似數(shù)學(xué)中的算術(shù)表達(dá)式)
每個(gè) C++ 表達(dá)式(帶有操作數(shù)的操作符、字面量、變量名等)可按照兩種獨(dú)立的特性加以辨別:**類型和值類別 **(value category)。每個(gè)表達(dá)式都具有某種非引用類型,且每個(gè)表達(dá)式只屬于三種基本值類別中的一種:純右值 (prvalue)、亡值 (xvalue)、左值 (lvalue)。
二.值類別
對(duì)于表達(dá)式來說:表達(dá)式是可以求值的,對(duì)表達(dá)式求值將得到一個(gè)結(jié)果,這個(gè)結(jié)果有兩個(gè)屬性:類型和值類別。
在C++11以后表達(dá)式按值類別分類,必然屬于以下三者之一:
- 左值
- 將亡值
- 純右值
其中,左值和將亡值合稱為泛左值,純右值和將亡值合稱為右值
三.左值
左值:能夠用&取地址的表達(dá)式為左值表達(dá)式
下列表達(dá)式是左值表達(dá)式:
- 變量、函數(shù)、模板形參對(duì)象 (C++20 起)或數(shù)據(jù)成員的名字,不論類型,例如 std::cin 或 std::endl。即使變量的類型是右值引用,由它的名字構(gòu)成的表達(dá)式仍是左值表達(dá)式;
- 返回類型是左值引用的函數(shù)調(diào)用或重載運(yùn)算符表達(dá)式,例如 std::getline(std::cin, str)、std::cout << 1、str1 = str2 或 ++it;
- a = b,a += b,a %= b,以及所有其他內(nèi)建的賦值及復(fù)合賦值表達(dá)式;
- ++a 和 --a,內(nèi)建的前置自增與前置自減表達(dá)式;
- *p,內(nèi)建的間接尋址表達(dá)式;
- a[n] 和 n[a],內(nèi)建的下標(biāo)表達(dá)式,當(dāng) a[n] 中的一個(gè)操作數(shù)是數(shù)組左值時(shí) (C++11 起);
- a.m,對(duì)象成員表達(dá)式,除了 m 是成員枚舉項(xiàng)或非靜態(tài)成員函數(shù),或者 a 是右值而 m 是對(duì)象類型的非靜態(tài)數(shù)據(jù)成員的情況;
- p->m,內(nèi)建的指針成員表達(dá)式,除了 m 是成員枚舉項(xiàng)或非靜態(tài)成員函數(shù)的情況;
- a.*mp,對(duì)象的成員指針表達(dá)式,其中 a 是左值且 mp 是數(shù)據(jù)成員指針;
- p->*mp,內(nèi)建的指針的成員指針表達(dá)式,其中 mp 是數(shù)據(jù)成員指針;
- a, b,內(nèi)建的逗號(hào)表達(dá)式,其中 b 是左值;
- a ? b : c,對(duì)某些 b 和 c 的三元條件表達(dá)式(例如,當(dāng)它們都是同類型左值時(shí),但細(xì)節(jié)見定義);
- 字符串字面量,例如 “Hello, world!”;
- 轉(zhuǎn)換到左值引用類型的轉(zhuǎn)型表達(dá)式,例如 static_cast<int&>(x);
- 返回類型是到函數(shù)的右值引用的函數(shù)調(diào)用表達(dá)式或重載的運(yùn)算符表達(dá)式;(C++11 起)
- 轉(zhuǎn)換到函數(shù)的右值引用類型的轉(zhuǎn)型表達(dá)式,如 static_cast<void (&&)(int)>(x)。(C++11 起)
性質(zhì):
- 可以通過內(nèi)建的取址運(yùn)算符取左值的地址:&++i[1] 及 &std::endl 是合法表達(dá)式。
- 可修改的左值可用作內(nèi)建賦值和內(nèi)建復(fù)合賦值運(yùn)算符的左操作數(shù)。
- 左值可以用來初始化左值引用;這會(huì)將一個(gè)新名字關(guān)聯(lián)給該表達(dá)式所標(biāo)識(shí)的對(duì)象。
四.純右值
滿足下列條件之一:
1.本身就是純粹的字面值,如3,false,12.13
2.求值結(jié)果相當(dāng)于字面值或是一個(gè)不具名的臨時(shí)對(duì)象
下列表達(dá)式是純右值表達(dá)式:
- (除了字符串字面量之外的)字面量,例如 42、true 或 nullptr;
- 返回類型是非引用的函數(shù)調(diào)用或重載運(yùn)算符表達(dá)式,例如 str.substr(1, 2)、str1 + str2 或 it++;
- a++ 和 a–,內(nèi)建的后置自增與后置自減表達(dá)式;
- a + b、a % b、a & b、a << b,以及其他所有內(nèi)建的算術(shù)表達(dá)式;
- a && b、a || b、!a,內(nèi)建的邏輯表達(dá)式;
- a < b、a == b、a >= b 以及其他所有內(nèi)建的比較表達(dá)式;
- &a,內(nèi)建的取地址表達(dá)式;
- a.m,對(duì)象成員表達(dá)式,其中 m 是成員枚舉項(xiàng)或非靜態(tài)成員函數(shù)[2],或其中 a 是右值且 m 是非- - 引用類型的非靜態(tài)數(shù)據(jù)成員 (C++11 前);
- p->m,內(nèi)建的指針成員表達(dá)式,其中 m 是成員枚舉項(xiàng)或非靜態(tài)成員函數(shù)[2];
- a.*mp,對(duì)象的成員指針表達(dá)式,其中 mp 是成員函數(shù)指針[2],或其中 a 是右值且 mp 是數(shù)據(jù)成員指針 (C++11 前);
- p->*mp,內(nèi)建的指針的成員指針表達(dá)式,其中 mp 是成員函數(shù)指針[2];
- a, b,內(nèi)建的逗號(hào)表達(dá)式,其中 b 是右值;
- a ? b : c,對(duì)某些 b 和 c 的三元條件表達(dá)式(細(xì)節(jié)見定義);
- 轉(zhuǎn)換到非引用類型的轉(zhuǎn)型表達(dá)式,例如 static_cast(x)、std::string{} 或 (int)42;
- this 指針;
- 枚舉項(xiàng);
- 非類型模板形參,除非它的類型是類或 (C++20 起)左值引用類型;
- lambda 表達(dá)式,例如 [](int x){ return x * x; };(C++11 起)
- requires 表達(dá)式,例如 requires (T i) { typename T::type; };(C++20 起)
- 概念的特化,例如 std::equality_comparable (C++20 起)
性質(zhì):
純右值不具有多態(tài):它所標(biāo)識(shí)的對(duì)象的動(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++是右值
前者,對(duì)i加1后再賦給i,最終的返回值就是i,所以,++i的結(jié)果是具名的,名字就是i;而對(duì)于i++而言,是先對(duì)i進(jìn)行一次拷貝,將得到的副本作為返回結(jié)果,然后再對(duì)i加1,由于i++的結(jié)果是對(duì)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是左值。而對(duì)&a而言,得到的是a的地址,相當(dāng)于unsigned int型的字面值,所以是純右值。
a+b、a&&b、ab 都是純右值
a+b得到的是不具名的臨時(shí)對(duì)象,而 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è)對(duì)象或?yàn)橐粋€(gè)已有對(duì)象賦值時(shí),會(huì)調(diào)用拷貝構(gòu)造函數(shù)或拷貝賦值運(yùn)算符來拷貝資源(所謂資源,就是指new出來的東西),而當(dāng)我們用一個(gè)右值(包括純右值和將亡值)來初始化或賦值時(shí),會(huì)調(diào)用移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符來移動(dòng)資源,從而避免拷貝,提高效率。當(dāng)該右值完成初始化或賦值的任務(wù)時(shí),它的資源已經(jīng)移動(dòng)給了被初始化者或被賦值者,同時(shí)該右值也將會(huì)馬上被銷毀(析構(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á)式:
- 返回類型為對(duì)象的右值引用的函數(shù)調(diào)用或重載運(yùn)算符表達(dá)式,例如 std::move(x);
- a[n],內(nèi)建的下標(biāo)表達(dá)式,它的操作數(shù)之一是數(shù)組右值;
- a.m,對(duì)象成員表達(dá)式,其中 a 是右值且 m 是非引用類型的非靜態(tài)數(shù)據(jù)成員;
- a.*mp,對(duì)象的成員指針表達(dá)式,其中 a 是右值且 mp 是數(shù)據(jù)成員指針;
- a ? b : c,對(duì)某些 b 和 c 的三元條件表達(dá)式(細(xì)節(jié)見定義);
- 轉(zhuǎn)換到對(duì)象的右值引用類型的轉(zhuǎn)型表達(dá)式,例如 static_cast<char&&>(x);
- 在臨時(shí)量實(shí)質(zhì)化后,任何指代該臨時(shí)對(duì)象的表達(dá)式。(C++17 起)
性質(zhì):
1.與右值相同。
2.與泛左值相同。
特別是,與所有的右值類似,亡值可以綁定到右值引用上,而且與所有的泛左值類似,亡值可以是多態(tài)的,而且非類的亡值可以有 cv 限定。
六.注意
1)字符串字面值是左值。
不是所有的字面值都是純右值,字符串字面值是唯一例外。
早期C++將字符串字面值實(shí)現(xiàn)為char型數(shù)組,實(shí)實(shí)在在地為每個(gè)字符都分配了空間并且允許程序員對(duì)其進(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)算對(duì)象時(shí)才需要運(yùn)算符連接,單獨(dú)的運(yùn)算對(duì)象也可以是表達(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ù)文章中都會(huì)詳細(xì)介紹。
⑤前提是該右值(如自定義的類X)有移動(dòng)構(gòu)造函數(shù)或移動(dòng)賦值運(yùn)算符可供調(diào)用(有時(shí)候是沒有的,關(guān)于這些知識(shí),后續(xù)文章在講移動(dòng)構(gòu)造函數(shù)和移動(dòng)賦值運(yùn)算符時(shí)會(huì)詳述)。
⑥在本文的例二中,如果將get_a_X()的返回值由X的右值引用改為X對(duì)象,則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)造的例子。
原文鏈接:https://blog.csdn.net/weixin_56935264/article/details/125351781
相關(guān)推薦
- 2024-03-03 layui彈窗編輯表單清空
- 2022-08-11 python面積圖之曲線圖的填充_python
- 2022-05-13 larvel8 批量刪除
- 2022-07-15 go學(xué)習(xí)筆記讀取consul配置文件詳解_Golang
- 2022-04-09 SpringBoot自定義Validated枚舉校驗(yàn)器
- 2021-12-04 淺談C++中const與constexpr的區(qū)別_C 語言
- 2022-11-04 C#使用BinaryFormatter類、ISerializable接口、XmlSerializer
- 2022-05-02 C/C++的文件IO函數(shù)你知道嗎_C 語言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支