1. 左右值和左右值引用
什么是左值、右值呢? 一種極不嚴謹的理解為:在賦值的時候,能夠被放到等號左邊的值為左值,放在右邊的值為右值。例如:
int sum(int x, int y){return x + y;}
int a = 1; //a為左值,常數1為右值
int b = a + a; //b為左值,表達式a+a為右值
int c = sum(a, a);//c為左值,但函數sum(a, a)返回值為右值
通過上面的例子,常數a
、表達式(a+a)
和函數sum(a+a)
返回值他們都是臨時值,這些值都保存在寄存器中,無法取到他們的地址;而對于a
、b
和c
為具體的變量名,存儲在內存中,可以取到其地址。因此一般情況下可以根據能否取到地址,來區分左值和右值。
在了解左值和右值之前,我們首先要知道表達式的概念: 由運算符和運算對象構成的計算式(類似數學中的算術表達式) 。表達式是可以求值的,因此根據表達式值的類別,可以對其進行分類(準確的來說,是表達式的結果的值類別,但我們一般不刻意區分表達式和表達式的求值結果,所以這里稱“表達式的值類別”。),C++11之后將表達式定義了五種類型:
- lvalue (Left-hand-side value,左值)
- prvalue (Pure rvalue,純右值)
- xvalue (eXpiring value,將亡值)
- rvalue (Right-hand-side value,右值)
- glvalue (Generalized lvalue,泛左值)
它們之間的關系如下圖所示:
C++11中將表達式按值類別可以分為 左值 、將亡值和 純右值 。其中,左值和將亡值合稱為泛左值,純右值和將亡值合稱為右值。
隨著移動語義(后面我們會詳細介紹)引入到 C++11 之中,值類別被重新進行了定義,C++之父Bjarne Stroustrup在《“New” Value Terminology》中給出以區別表達式的兩種獨立的性質:
- 擁有身份 (identity):可以確定表達式是否與另一表達式指代同一實體,例如通過比較它們所標識的對象或函數的(直接或間接獲得的)地址;
- 可被移動 :移動構造函數、移動賦值運算符或實現了移動語義的其他函數重載能夠綁定于這個表達式。
C++11 中:
- 擁有身份且不可被移動的表達式被稱作 左值 (lvalue)表達式;
- 擁有身份且可被移動的表達式被稱作將****亡值 (xvalue)表達式;
- 不擁有身份且可被移動的表達式被稱作 純右值 (prvalue)表達式;
1.1 左值
一般情況下,左值我們可以簡單地理解理解為: 能夠使用&
取地址的表達式 。
常見的左值有:
- 變量名
- 函數名
- 返回左值引用的函數調用
- 前置自增/減的運算符鏈接的表達式(如
++i
/--i
) - 內置的賦值表達式(如
a=b
,a+=1
) - 字符串等。
【 注 :字符串是可以取地址的,因此字符串常量也屬于左值】
1.2 純右值
純右值:表達式本身就是純粹的字面值(如1
,ture
,1.0
);或者,該表達式求值結果相當于一個字面值或一個不具名的臨時對象。
常見的純右值有:
- 除字符串字面值以外的字面值
- 返回非引用類型的函數調用
- 后置自增/減的運算符鏈接的表達式(如
i++
/i--
) - 算術/邏輯/比較表達式(如
a+b
,a&&b
,a==b
) - 取地址表達式(如
&a
)
1.3 將亡值
將亡值是在C++11中引進來的,顧名思義,就即將銷毀的東西。將亡值的產生與右值引用的產生而引起的,對于將亡值我們常用到的有:
- 返回類型是右值引用的函數調用或重載運算符的表達式(如
std::move(x)
) - 轉換為右值引用的轉換函數的調用表達式(如
static
)(a)
1.4 左右值引用
左值引用就是對左值的引用。它的形式如:T&
,根據const屬性可以分為兩種:
- const左值引用
- 非const左值引用
例如:
int a = 1;
int& la = a;//la為a的左值引用(非const左值引用)
la = 2;//la為非const左值引用,可以修改它的值
const int& c_la = a;//c_la為a的左值引用(const左值引用)
c_la = 2;//該語法錯誤,c_la為const左值引用,不可以修改它的值
右值引用就是對右值的引用,通過T&&
來表示。右值的引用只能綁定到右值上。
2. 移動語義
在未出現右值引用之前,我們在函數調用傳參的時候,在某些時候可以使用按引用傳遞參數,減少參數多的拷貝對資源的消耗,提高程序的運行效率。當我們在處理包含大量數據的對象時,移動語義顯的尤為重要。
2.1 std::move
如何將一個左值轉換為一個右值呢?C++11在頭文件utility中聲明了std::move()
函數,該函數的作用就是類型轉換,通過它,我們可以 把一個左值,將其標記為右值。move()
不做任何資源轉移的操作,只是產生一個將亡值表達式來標識參數x
,其完全等同于static_cast
。例如:
int a = 1;
int&& r_a = a; //錯誤,右值引用只能綁定到右值上,而a是一個左值
int&& r_a = std::move(b); //正確, std::move(a) 是一個右值,可以用右值引用綁定
2.2 移動構造函數
一個類 T
的首個形參是 T&&、const T&&
、volatile T&&
或 const volatile T&&
,且沒有其他形參,或剩余形參都有默認值。
具體的形式如下:
T (T &&) //移動構造函數的典型聲明形式
T (T &&) = default; //強制編譯器生成移動構造函數。
T (T &&) = delete; //避免隱式生成移動構造函數。
示例:
#include < string >
#include < iostream >
#include < utility >
class A
{
private:
std::string s;
public:
A(std::string str = "A()") : s(str) {std::cout<
2.3 移動賦值運算符
一個類 T
的移動賦值運算符是名為 operator=的非模板非靜態成員函數,它接受恰好一個 T&&
、const T&&
、volatile T&&
或 const volatile T&&
類型的形參。
具體的形式如下:
T & T ::operator= (T &&) //移動賦值運算符的典型聲明
T & T ::operator= (T &&) = default; //強制編譯器生成移動賦值運算符
T & T ::operator= (T &&) = delete; //避免隱式移動賦值
示例:
#include < string >
#include < iostream >
#include < utility >
class A
{
private:
std::string s;
public:
A(std::string str = "A()") : s(str) {std::cout<
-
轉換器
+關注
關注
27文章
8728瀏覽量
147444 -
C++語言
+關注
關注
0文章
147瀏覽量
7009
發布評論請先 登錄
相關推薦
評論