左值和右值
- 左值和右值的定義
- 左值
- 右值
- 擴展
- 左值參考和右值參考
- 概念
- 左值參考系結右值
- 右值參考系結左值
- 實體
- 給右值參考傳入一個左值
- 臨時變數
- 參考資料
左值和右值是C++比較重要也比較復雜的知識點,可能學習C++很久的人對這倆個概念都不是很熟悉!我在大學中對他們也是沒有概念,最近學到這里,發現網上對這倆的解釋也千奇百怪,所以經過三天的學習和整理,整理出一份還不錯的筆記,建議大家重點看這一篇,不要被其他文章帶跑偏了,

左值和右值的定義
在C++11中所有的值必屬于左值、右值兩者之一,右值又可以細分為純右值、將亡值,在C++11中可以取地址的、有名字的就是左值,反之,不能取地址的、沒有名字的就是右值(將亡值或純右值),
舉個例子,int a = b + c,a 就是左值,其有變數名為a,通過&a可以獲取該變數的地址;運算式b+c、函式int func()的回傳值就是右值,在其被賦值給某一變數前,我們不能通過變數名找到它,&(b+c)這樣的操作則不會通過編譯,
左值
左值與右值這兩概念是從 c 中傳承而來的,在 c 中,左值指的是既能夠出現在等號左邊也能出現在等號右邊的變數(或運算式)
右值
右值指的則是只能出現在等號右邊的變數(或運算式).
擴展

在理解C++11的右值前,先看看C++98中右值的概念:C++98中右值是純右值,純右值指的是臨時變數值、不跟物件關聯的字面量值,臨時變數指的是非參考回傳的函式回傳值、運算式等,例如函式int func()的回傳值,運算式a+b;不跟物件關聯的字面量值,例如true,2,”C”等,
C++11對C++98中的右值進行了擴充, 在C++11中右值又分為純右值(prvalue,Pure Rvalue)和將亡值(xvalue,eXpiring Value),其中純右值的概念等同于我們在C++98標準中右值的概念,指的是臨時變數和不跟物件關聯的字面量值;將亡值則是C++11新增的跟右值參考相關的運算式,這樣運算式通常是將要被移動的物件(移為他用),比如回傳右值參考T&&的函式回傳值、std::move的回傳值,或者轉換為T&&的型別轉換函式的回傳值,
將亡值可以理解為通過“盜取”其他變數記憶體空間的方式獲取到的值,在確保其他變數不再被使用、或即將被銷毀時,通過“盜取”的方式可以避免記憶體空間的釋放和分配,能夠延長變數值的生命期,(通過右值參考來續命)
左值參考和右值參考
首先在C++11之前,只有左值參考,在C++11之后,才有右值參考,
我們學習右值參考之前,一定要帶著問題去學習去思考,“引入一種額外的參考型別當然增加了語言的復雜性,但是我們為什么要增加復雜性呢,肯定同時也會帶來優越性!”
概念
左值參考就是對一個左值進行參考的型別,右值參考就是對一個右值進行參考的型別,事實上,由于右值通常不具有名字,我們也只能通過參考的方式找到它的存在,
右值參考和左值參考都是屬于參考型別,無論是宣告一個左值參考還是右值參考,都必須立即進行初始化,而其原因可以理解為是參考型別本身自己并不擁有所系結物件的記憶體,只是該物件的一個別名,左值參考是具名變數值的別名,而右值參考則是不具名(匿名)變數的別名,
首先我們明確了最普通的左右值參考是:
左值參考:對一個左值進行參考
右值參考:對一個右值進行參考【C++11之后才引入右值參考】
那么還有沒有一些特殊的參考呢?答案是有的!
左值參考系結右值
關于右值,在 c++11 以前有一個十分值得關注的語言的特性:右值能被 const 型別的參考所指向,所以如下代碼是合法的,
const cs& ref = get_cs();
而且準確地說,右值只能被 const 型別的 reference 所指向,非 const 的參考則是非法的:
1. // error
2. cs& ref = get_cs();
注意:常量左值參考是個“萬能”的參考型別,它可以接受非常量左值、常量左值、右值對其進行初始化,不過常量左值所參考的右值在它的“余生”中只能是只讀的,相對地,非常量左值只能接受非常量左值對其進行初始化,
int &a = 2; # 左值參考系結到右值,編譯失敗
int b = 2; # 非常量左值
const int &c = b; # 常量左值參考系結到非常量左值,編譯通過
const int d = 2; # 常量左值
const int &e = c; # 常量左值參考系結到常量左值,編譯通過
const int &b =2; # 常量左值參考系結到右值,編譯通過
右值參考系結左值
右值值參考通常不能系結到任何的左值,要想系結一個左值到右值參考,通常需要std::move()將左值強制轉換為右值,例如:
int a;
int &&r1 = a; # 編譯失敗
int &&r2 = std::move(a); # 編譯通過
int &&r2 = 2; # 編譯通過
實體
#include <iostream>
void process_value(int& i)
{
std::cout << "LValue processed: " << i << std::endl;
}
void process_value(int&& i) //右值參考
{
std::cout << "RValue processed: " << i << std::endl;
}
int main()
{
int a = 0;
process_value(a);
process_value(1);
}
我們在main函式中,非常正常地傳入了一個左值和一個右值,所以輸出結果也是正確的!

給右值參考傳入一個左值
問題的來源:
r2是一個右值參考,但是r2本身是不是左值呢?C++11對此做出了區分:
如果它有一個名字,那么它是一個左值,否則,它是一個右值,
int main()
{
int a = 0;
process_value(a);
int&& x = 3;
process_value(x);
}
x 是一個右值參考,指向一個右值3,但是由于x是有名字的,所以x在這里被視為一個左值,所以在函式多載的時候選擇為第一個函式,

右值參考的意義 我們如果想在實際開發中用到右值參考,我們就要搞清楚為什么我們需要右值參考,也就是右值參考的意義! 直觀意義 為臨時變數續命,也就是為右值續命,因為右值在運算式結束后就消亡了,如果想繼續使用右值,那就會動用昂貴的拷貝建構式,
這里引出了臨時變數:
臨時變數
C++ 中的臨時變數指的是那些由編譯器根據需要在堆疊上產生的,沒有名字的變數,主要的用途主要有兩類:
- 函式的回傳值,
- 型別轉換時的中間變數
一般來說,C++ 中的臨時變數在運算式結束之后 (full expression) 就被會銷毀,但也有例外的時候,如果這個臨時變數被用來初始化一個參考的話,那這個臨時變數的生命周期就會被延長,直到參考被銷毀,從而不會因此產生懸空(dangling)的參考,
也就是說,一旦一個臨時變數被參考,它的生命周期就變得和參考它的變數一樣長!
右值參考是用來支持轉移語意的,
轉移語意可以將資源 ( 堆,系統物件等 ) 從一個物件轉移到另一個物件,這樣能夠減少不必要的臨時物件的創建、拷貝以及銷毀,能夠大幅度提高C++應用程式的性能,臨時物件的維護 ( 創建和銷毀 ) 對性能有嚴重影響,
轉移語意是和拷貝語意相對的,可以類比檔案的剪切與拷貝,當我們將檔案從一個目錄拷貝到另一個目錄時,速度比剪切慢很多,通過轉移語意,臨時物件中的資源能夠轉移其它的物件里,
在現有的 C++ 機制中,我們可以定義拷貝建構式和賦值函式,要實作轉移語意,需要定義轉移建構式,還可以定義轉移賦值運算子,對于右值的拷貝和賦值會呼叫轉移建構式和轉移賦值運算子,如果轉移建構式和轉移拷貝運算子沒有定義,那么就遵循現有的機制,拷貝建構式和賦值運算子會被呼叫, 普通的函式和運算子也可以利用右值參考運算子實作轉移語意,
參考資料
【1】https://www.cnblogs.com/catch/p/3500678.html
【2】https://mp.weixin.qq.com/s?src=11×tamp=1626703408&ver=3200&signature=-8fSD48eWwkkMchC3DjpghdBHkf4GySAjvEFSs1PsiwZfOP11nqAzk6XszTE9XQXFkGp7OP5G1gLLAmAehwOlqpw8yMwA0X3FMKfMrwzQE4-HrKQHSgAbIYDp6s-OP&new=1
【3】臨時變數:https://www.cnblogs.com/catch/p/3251937.html
【4】牛逼:http://c.biancheng.net/view/7847.html
寫作不易,希望給個關注,有問題可以隨時私聊交流
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/290129.html
標籤:其他

