當前位置:首頁 > 公眾號精選 > CPP開發(fā)者
[導讀]↓推薦關注↓表達式是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,doublestd::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ù)重載能夠綁定到這個表達式。
由此,C 11中對于這五種類別定義如下:

  • lvalue是指:擁有身份且不可被移動的表達式。
  • xvalue是指:擁有身份且可被移動的表達式。
  • prvalue是指:不擁有身份且可被移動的表達式。
  • glvalue是指:擁有身份的表達式,lvalue和xvalue都是glvalue。
  • rvalue是指:可被移動的表達式。prvalue和xvalue都是rvalue。
這么說起來還是有些拗口,不過其實顛來倒去就是兩個特征的“是”與“否”,所以通過一個2x2的表格就很容易描述清楚了:


擁有身份(glvalue)不擁有身份
可移動(rvalue)xvalueprvalue
不可移動lvalue不存在
注:不存在不擁有身份也不可移動的表達式。

我們可以通過下面這個圖來記憶五種類別的關系:

img
每種值類別都有其關聯(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進行取地址操作。例如:
本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內容侵犯您的權益,請及時聯(lián)系本站刪除。
關閉
關閉