Value Category(值類別)- 1 / 2
這筆記從 C 原本對 lvalue 與 rvalue 的使用與,再到 C++ 11 標準時使用的 lvalue 與 rvalue 。我覺得 Value Category 是入門 Modern C++ 需要釐清的重要觀念。了解值類別也可以幫助了解編譯器與物件生命週期中的細節,在寫扣上也會有些幫助。
首先要記得的是 lvalue 和 rvalue 這種東西不是一種 C++ 語言中的 feature ,它們是 expression ,表達出特定的 semantic (語意),這樣的語意除了 C/C++ 之外也可以套用在其他程式語言上。
為了方便閱讀,分成兩個部分。
第一部分:Back in C & Temporary Materialization Conversion
第二部分:Modern C++ Value Category
Back in C
以前的定義非常簡單,等號左邊就是 lvalue ,等號右邊就是 rvalue 。從這出發我們可以歸類:
- lvalue 在等號右邊被 “assign”(賦值),而能被賦值的東西就是 “object”(物件)。這裡 lvalue 就是表達出物件的 expression。
- 在 C 中很簡單,rvalue 就是其他 lvalue 不包含的範圍。
可以得到一個簡單的結論:一個 assignment statement 中等號的左右側分別是 lvalue 與 rvalue,而 lvalue 必須可以被 refer 到一個物件。
為什麼這樣區分
目的是想要讓編譯器知道「rvalue 是不需要佔據空間的值」。
如果知道了這種事,編譯器可以做一些優化:原本不知道這個值是不需要佔據空間時,編譯器認為該物件的週期是由 runtime 決定的,所以創造一個 global storage 來儲存值,並在 assignment 時將值載入並進行賦值。如果知道這個值不需要作為一個物件存在時,assembly 中有辦法輸入「immediate」作為值來達到 assignment 的目的。這樣 global storage 的部分被省去了,也可以減少 code bloat 的情況發生。
無法作為 immediate 存在時
上述二分的前提是 rvalue 的值都是一些 immediate 像是 x = f(1)
這個句法中, 1
這個值是被 ISA 定義出可以直接被 compiler 辨識並賦予的。然而當值不再是基本型別時,因為無法被直接創造並賦值,他們就無法滿足作為 R-Value 的條件,就需要想辦法解決這樣的問題。
像是 String literal 儘管可能是 rvalue,但是它卻需要佔有記憶體空間。包括 String literal 以及其他自定義類別,我們稱呼他們為 Class-type R-Value。
我們要解決「因 expression 而產生 temporary object 的記憶體配置」這一件事。
Temporary Object
一個最簡單的例子就當一個 expression 全部由 lvalue 組成時,這時 expression 所表示出來的值不屬於其中任何一個 lvalue,這樣的 temporary object 是 rvalue。
繼續延續剛剛 class type 提出的問題,先提出以下分類:
- lvalue 佔有實際記憶體空間
- Non-class rvalue 不佔有記憶體空間
Class-type rvalue 需要佔有記憶體空間,因為在存取 class 內部結構時,需要一個 base addresss 找到結構的位址。這時我們就需暫時創造一些暫時的空間來存放這樣的 class-type immediate ,而在之後 out-of-scope 時會把這些暫時分配的記憶體會被移除(pop stack)。
因此基於上面的假設, rvalue 在執行期間有可能會一度佔有空間,把這個值暫時放到 register 上面之類的。由此我們再細分 rvalue 成兩種:
- prvalue (Pure rvalue):不佔有任何記憶體位置
- xvalue (eXpiring rvalue):會在程式中暫時佔有記憶體位置
C++ 定義把一個 prvalue 轉換成 xvalue (temporary object) 的行為稱作 “Temporary Materialization Conversion” (TMC)。
繼續前閱讀 第二部分:Reference binding & Modern C++ Value Category
Original link: eopXD