C 中的值類別
時間:2021-09-23 16:00:57
手機看文章
掃描二維碼
隨時隨地手機看文章
[導讀]↓推薦關注↓表達式是C語言的基石。每個表達式都有兩個屬性:類型(type)和值類別(valuecategory)。前者是大家都熟悉的,但是后者卻可能是我們不太在意的。本文的目的是介紹與值類別相關的一些知識。前言本文是C基礎系統(tǒng)文章中的一篇,將介紹C中的值類別,以及與之相關的一些概...
↓推薦關注↓
表達式是C 語言的基石。每個表達式都有兩個屬性:類型(type)和值類別(value category)。前者是大家都熟悉的,但是后者卻可能是我們不太在意的。本文的目的是介紹與值類別相關的一些知識。
前言
本文是C 基礎系統(tǒng)文章中的一篇,將介紹C 中的值類別,以及與之相關的一些概念。表達式與值類別
C 的程序由一系列的表達式(expressions)構成。表達式是運算符和操作數(shù)的序列,表達式指定一項計算。例如:2 2
或者 std::cout << "Hello World" << std::endl
都是表達式。每個表達式有兩個互相獨立但是非常重要的屬性:- 類型(type)。類型是我們很熟悉的概念,
int
,double
和std::string
這些都是類型。類型確定了表達式可以進行哪些操作。 - 除了類型之外,還有一個稱之為值類別(value category)的屬性,卻可能是我們平時不太注意的。
type和category在中文中似乎都可以翻譯成“類型”。但在本文中,為了區(qū)分它們,統(tǒng)一將type翻譯成“類型”,category翻譯成“類別”。
為什么要懂這些東西?
不管你在不在意,每個表達式都屬于三種值類別(prvalue,xvalue,lvalue)中的一種。值類別可以影響表達式的含義,例如:你應該知道這個表達式是沒有意義的:3 = 4
,它甚至編譯不過。但你可能說不出來為什么編譯器會認為它編譯不過。如果你使用gcc編譯器,它的報錯如下:?error:?lvalue?required?as?left?operand?of?assignment
這個報錯中的lvalue
就是數(shù)字表達式3
的值類別。再者,值類別還會影響函數(shù)的重載:當某個函數(shù)有兩種重載可用,其中之一接受右值引用的形參而另一個接受 const 的左值引用的形參時,右值將被綁定到右值引用的重載之上。如果你不明白這里提到的“左值引用”和“右值”是指什么的話請不要擔心,這就是本文所要說明的。從左值和右值說起
最初的時候,只有左值(lvalue)和右值(rvalue)這兩個術語。它們源于C 的祖先語言:[CPL](https://en.wikipedia.org/wiki/CPL_(programming_language "CPL"))。lvalue之所以叫l(wèi)value,是因為它常常出現(xiàn)在等號的左邊(left-hand side of an assignment)。同樣,rvalue是因為它常常出現(xiàn)在等號的右邊(right-hand side of an assignment)。回顧一下上面的3 = 4
編譯報錯,就是因為編譯器要求等號的左邊得是一個lvalue,而數(shù)字3
其實是一個rvalue,所以這個是無法通過編譯的。C語言遵循了相似的分類法,但是否需要等號賦值已經(jīng)不再重要。在C語言中,標識一個對象的表達式稱之為左值,不過lvalue已經(jīng)是“l(fā)ocator value”的簡寫,因為lvalue對應了一塊內存地址。你可以簡單的理解為:左值對應了具有內存地址的對象,而右值僅僅是臨時使用的值。例如int a = 1
中,a
是左值,1
是右值。C 11中的值類別
C 中對于值類別的定義也經(jīng)歷一些變化。從C 11標準開始,值類別早以不止是lvalue和rvalue兩種這么簡單。但情況也不算太壞,因為主要的值類別有:lvalue,prvalue 和 xvalue三種。加上兩種混合類別:glvalue和rvalue,一共有五種。我們來看一下它們的定義:- A glvalue(generalized lvalue) is an expression whose evaluation determines the identity of an object, bit-field, or function.
- A prvalue(pure rvalue) is an expression whose evaluation initializes an object or a bit-field, or computes the value of an operand of an operator, as specified by the context in which it appears, or an expression that has type cv void.
- An xvalue(eXpiring value) is a glvalue that denotes an object or bit-field whose resources can be reused (usually because it is near the end of its lifetime).
- An lvalue is a glvalue that is not an xvalue.
- An rvalue is a prvalue or an xvalue.
- 是否擁有身份(identity):可以確定表達式是否與另一表達式指代同一實體,例如比較它們所標識的對象或函數(shù)的(直接或間接獲得的)地址;
- 是否可被移動(具體見下文):移動構造函數(shù)、移動賦值運算符或實現(xiàn)了移動語義的其他函數(shù)重載能夠綁定到這個表達式。
- lvalue是指:擁有身份且不可被移動的表達式。
- xvalue是指:擁有身份且可被移動的表達式。
- prvalue是指:不擁有身份且可被移動的表達式。
- glvalue是指:擁有身份的表達式,lvalue和xvalue都是glvalue。
- rvalue是指:可被移動的表達式。prvalue和xvalue都是rvalue。
擁有身份(glvalue) | 不擁有身份 | |
---|---|---|
可移動(rvalue) | xvalue | prvalue |
不可移動 | lvalue | 不存在 |
注:不存在不擁有身份也不可移動的表達式。我們可以通過下面這個圖來記憶五種類別的關系:每種值類別都有其關聯(lián)的性質,這些性質決定了表達式可以如何使用。
glvalue
glvalue是擁有身份的表達式,它對應了一塊內存地址。glvalue有l(wèi)value和xvalue兩種形式,具體的示例見下文。glvalue具有以下一些特性:- glvalue可以自動轉換成prvalue。例如:
int a = b
,等號右邊的lvalue會自動轉換成rvalue。 - glvalue可以是多態(tài)的(polymorphic),它所對應了動態(tài)類型和靜態(tài)類型可以不一樣,例如:一個指向子類的父類指針。
- glvalue可以是不完整類型,只要表達式允許。例如:由前置聲明但未定義的類類型。
rvalue
rvalue是指可以移動的表達式。prvalue和xvalue都是rvalue,具體的示例見下文。rvalue具有以下特征:- 無法對rvalue進行取地址操作。例如: