c 中的左值與右值

2021-12-29 19:39:03 字數 2849 閱讀 7033

左值(lvalue),右值(rvalue)是乙個比較晦澀的概念,有些人可能甚至沒有聽過,但這個概念到了c++11後,卻變得十分重要,它們是理解move(),forward()等新語義的基礎。

那什麼是左值右值呢?

左值與右值這兩概念是從c中傳承而來的,在c中,左值指的是能夠出現在等號左邊及右邊的變數(表示式), 右值指的是只能出現在等號右邊的變數(表示式).

複製**

int a;

int b;

a = 3;

b = 4;

a = b;

b = a;

//不合法。

3 = a;

a+b = 4;

複製**

通常來說,有名字的變數就是左值(如上面例子中的a, b),而由運算(加減乘除,函式呼叫返回值等)產生的中間結果(沒有名字)就是右值,如上的3+4, a + b等。

我們暫且可以認為:左值就是在程式中能夠尋值的東西,右值就是沒法取到它的位址的東西(不完全準確)。

如上概念到了c++中,就變得稍有不同。

在c++中,每乙個表示式都會產生乙個左值,或者右值,相應的,該表示式也就被稱作「左值表示式」,「右值表示式」。對於內建的資料型別來說(build-in types),左值右值的概念和c的沒有太多不同,不同的地方在於自定義的型別。

而且這種不同比較容易讓人混淆:

1)對於內建的型別,右值是不可被修改的(non-modifiable),也不可被const, volatile所修飾(cv-qualitification ignored)

2)對於自定義的型別(user-defined types), 右值卻允許通過它的成員函式進行修改。

對於1),這是和c是一致的,2)卻是c++中所獨有, 因此,如果你看到c++中如下的寫法,也許有會些驚訝:

複製**

class cs

cs& operator=(const cs& other)

int get_i() const

void change(int i)

private:

int i_;}; 

cs get_cs()  

int main()

複製**

這個特性多少有些奇怪,通常來說,c++中的自定義型別是應該設計地盡量和內建型別一樣才對的,但這個特性卻偏偏違背了這個原則。

對於這個特性,我們其實可以這樣想,也許會好理解點:自定義型別允許有成員函式,而通過右值呼叫成員函式是被允許的,但成員函式有可能不是const型別,因此通過呼叫右值的成員函式,也就可能會修改了該右值,done!  

關於右值,還有乙個需要注意的地方是:右值能被const型別的引用所指向

const cs& ref = get_cs();

而且只能被const 型別的reference所指向:

//error 

cs& ref = get_cs();

當乙個右值被const reference指向時,它的生命週期就被延長了,這個用法我在前面一篇部落格裡講到過它的相關應用,點這。

這裡暗藏邏輯其實就是:右值不能直接轉化成左值(但左值可以轉化為右值).  

上面提到的這兩個特性:

1)允許呼叫成員函式。

2)只能被const reference指向。

導致了一些比較有意思的結果,比如:

複製**

void func(cs& c)

//error

func(get_cs());

//正確

func(get_cs() = get_cs());

複製**

其中:func(get_cs() = get_cs());能夠正確的原因就在於,cs的成員函式operator=() 返回的是cs&!  

不允許非const reference 引用rvalue並不是完美的,它事實上也引起了一些問題,比如說拷貝建構函式的介面不一致了,這是什麼意思呢?

複製**

class cs; 

// 另一種寫法

class cs2

;複製**

上面兩種寫法的不同之處就在於引數,乙個是const reference,乙個是非const.

通常來說,如果不需要修改傳進來的引數,我們往往就按const reference的寫法,但對於copy constructor來說,經常是需要修改引數的值,比如auto_ptr。

複製**

// 類似auto_ptr

class auto_ptr

private:

void*  ptr_;

};複製**  

所以,對於auto_ptr來說,它的copy constructor傳的引數是non const reference。

這個種寫法本來應該被鼓勵的,non const reference比const reference更能靈活應對各種情況,從而保持一致的介面型別。

但如果拷貝建構函式寫成這樣子,卻又對rvalue的使用帶來了極大的不變,如前面所講的例子,rvalue不能被non const reference所引用,所以像auto_ptr的這樣的copy constructor就不能傳入rvalue.

//錯誤

auto_ptr p(get_ptr());

// operator=() 同理,錯誤。

auto_ptr p = get_ptr();

這也是auto_ptr很不好用的其中乙個原因。

為了解決這個問題,c++11中引入了一種新的引用型別,該種引用型別是專門用來指向rvalue的,有了這種新型別,在c++11中,lvalue 和rvalue的引用型別從此區分了開來,而在之前,它們是一樣的。

因為有了這種新的型別,接著就引出了c++11中新的語義,move(), forward()等,這兒先賣個關子,我們下次再講。  

C 中左值與右值

讓我們先看看一些常見的左值和右值舉例 int var 6 var is a lvalue const int var 6 var is a nonmodifiable lvalue char str hello,world str is a nonmodifiable lvalue hello,wo...

C 中左值與右值

讓我們先看看一些常見的左值和右值舉例 int var 6 var is a lvalue const int var 6 var is a nonmodifiable lvalue char str hello,world str is a nonmodifiable lvalue hello,wo...

C 中的左值與右值

1.c 左值與右值概念 變數和文字常量都有儲存區,並且有相關的型別,區別在於變數是可定址的 對於每個變數,都有2個值與其相關聯 1 資料值,儲存在某個記憶體位址中,也稱右值 rvalue 右值是被讀取的值 read value 文字常量和變數都可被用於右值。2 位址值,即儲存資料值的那塊記憶體位址,...