C++11中對LValue和RValue的界定更加詳細而合理(但是也更加麻煩了)
1章節:C++11的新分類介紹
2章節:不同值之間的轉換
3章節:對一些常見的表達式,如何區分他們是什么值?
1. 表達式分類
所謂Value,是對表達式而言的。一個表達式可以分為以下幾種Value,下邊詳細說明
1.1. lvalue(左值)
lvalue指代一個函數或者對象。例如:
- E是指針,則*E是lvalue
- 一個函數的返回值是左值引用,其返回值是lvalue。例如int& foo();
1.2. xvalue(expiring value,臨終值)
xvalue指代一個對象,但是和lvalue不同,這個對象即將消亡。具體來說,xvalue是包含了右值引用的表達式。因為右值引用是C++11新引入的東西,所以xvalue也是一個新玩意。例如:
- 一個函數的返回值是右值引用,其返回值是xvalue。例如int&& foo();
1.3. glvalue(generalized lvalue,泛左值)
glvalue即lvalue和xvalue的統稱。
1.4. rvalue(右值)
rvalue是xvalue和prvalue的統稱。因為引入了右值引用,rvalue的定義在C++中被擴大化了。
1.5. prvalue(pure rvalue,純右值)
prvalue指代一個臨時對象、一個臨時對象的子對象或者一個沒有分配給任何對象的值。prvalue即老標准中的rvalue。例如:
- 一個函數的返回值是平常類型,其返回值是rvalue。例如int foo();
- 沒有分配給任何對象的值。如5.3,true。
2. 表達式值類型的轉換
glvalue → prvalue
其實表達式的轉換,只有以上一途,也就是
- lvalue → prvalue
- xvalue → prvalue
2.1. lvalue → prvalue
這個很常見:glvalue(lvalue和xvalue)可以隱式轉換為prvalue來滿足需求。例如:
int& foo(int val) // 函數接受prvalue { int a = val + 1; return a; } int main() { int i = 5; cout << foo(4) << endl; // 傳遞進去一個prvalue cout << foo(i) << endl; // 傳遞進去一個lvalue cout << foo(foo(i)) << endl; // foo(i)返回lvalue,傳遞至外層函數 system("pause"); }
當然,這個函數寫的不大好,不應該將臨時對象傳遞出函數的~
如果將foo改寫為下面這樣,上邊的代碼就不能工作了,因為prvalue→lvalue是不成滴~
int& foo(int val) { return val + 1; }
2.2. cv-qualifier的解除
在其他轉換中,const轉換是不能去除的,但是glvalue到prvalue的轉換是可以去除的。這個說辭比較晦澀,其實十分常見,比如:
const int a = 5; int b = a; // b = ? 這個需要右值,所以把a先轉為右值,同時丟棄了const限定詞
3. 表達式值類型的判定
C++的每一個表達式都有其值類型,換句話說,任何表達式,都屬於以下三者之一:lvalue,xvalue,prvalue。
如果是簡單的表達式自然很好理解,如上一節所舉的變量、常量以及返回值。但是大部分情況都要復雜得多。
2.1. C++內建操作的判定
這個就要具體操作具體分析了。C++的內建操作對操作數的類型做出了詳細的界定,對整個操作代表的類型也做出了詳細的界定。例如:
分配運算 op1 = op2。op1是lvalue,op2是rvalue。整個操作是lvalue
2.2. 用戶定義的操作
用戶定義操作,無非就是自建函數。(恩,沒錯,C++的操作符重載也是函數重載~~)操作數等價於函數參數,操作yield值相當於函數返回值。其類型都由用戶界定。例如:
- int foo(int); 參數是prvalue,返回值是prvalue。
- int& foo(int); 參數是prvalue,返回值是lvalue。u