術語與慣例 (Terms and Conventions)
Overview Table
| 術語 | 一句話定義 | 關鍵提醒 |
|---|---|---|
| lvalue | 可取得位址的 expression,通常對應具名物件 | 型別與 value category 無關:type T 可有 lvalue 也可有 rvalue |
| rvalue | 不可取位址的 expression,通常是暫時物件 | rvalue 代表可被 move 的物件 |
| argument | 呼叫端傳入的 expression | 可以是 lvalue 或 rvalue |
| parameter | 函式宣告中的形式參數 | 一律是 lvalue(即使型別是 T&&) |
| basic guarantee | 例外拋出後:invariant 不破壞、資源不洩漏 | exception safe 的最低門檻 |
| strong guarantee | 例外拋出後:程式狀態回到呼叫前 | 比 basic 更強,成本可能較高 |
| function object | 支援 operator() 的物件(廣義:可用函式呼叫語法者) |
狹義來自 C++98,廣義來自 C++11 |
| closure | lambda expression 產生的 runtime 物件 | 書中常將兩者統稱 lambda |
| declaration / definition | 引入名稱 vs 提供儲存/實作細節 | definition 也算 declaration |
| signature | 宣告中「參數型別 + 回傳型別」的部分 | 不含名稱,也不含 noexcept/constexpr |
| deprecated | 標準委員會標記為待淘汰的特性 | 如 std::auto_ptr → 改用 std::unique_ptr |
| undefined behavior (UB) | 標準不保證任何 runtime 行為 | 越界索引、解參考未初始化 iterator、data race |
| raw / smart pointer | 內建指標(如 new 回傳)vs 有智慧的包裝 |
smart pointer 通常 overload -> 與 * |
lvalue vs rvalue:move semantics 的地基
C++11 最普及的特性是 move semantics,其基礎就是區分 expression 是 lvalue 還是 rvalue:rvalue 代表有資格被 move 的物件,lvalue 一般不行。概念上(實務未必),rvalue 對應函式回傳的暫時物件,lvalue 對應可指名參照的物件(具名變數、透過指標或 lvalue reference 取得者)。
能對 expression 取位址(&) → 通常是 lvalue;不能取位址 → 通常是 rvalue。
class Widget {
public:
Widget(Widget&& rhs); // rhs 的「型別」是 rvalue reference,
// ... // 但 rhs 本身是 lvalue(具名、可取位址)
};
- 取位址判別法只是 heuristic(書中用「typically / usually」),並非嚴格定義。
- 所有 parameter 都是 lvalue——即使其型別是 rvalue reference(如上例
rhs)。在Widget的 move constructor 內對rhs取位址完全合法。這是 Item 23/25 中std::move存在的根本原因。
copy 的用語:以同型別物件初始化新物件時,新物件都稱為原物件的 copy——即使它是經由 move constructor 建成的。C++ 沒有術語區分「copy-constructed copy」與「move-constructed copy」。
void someFunc(Widget w); // w 以 pass-by-value 傳遞
Widget wid;
someFunc(wid); // w 是 wid 的 copy,經 copy construction 建立
someFuncmove(wid); // w 仍稱為 wid 的 copy,但經 move construction 建立
「rvalue 的 copy 用 move 建構、lvalue 的 copy 用 copy 建構」只是一般情形。若只知道某物件是另一物件的 copy,無法斷言建構成本——必須知道傳入的是 lvalue 還是 rvalue,以及該型別 move/copy 的實際代價。
Argument vs Parameter 與 Exception Safety
argument 是呼叫端傳入的 expression(可為 lvalue 或 rvalue);parameter 是函式內的具名變數(一律是 lvalue)。argument 用來初始化 parameter:
呼叫端 函式端
──────── ────────
someFunc(wid); argument = wid(lvalue)
someFuncmove(wid)); argument = std::move(wid(rvalue)
│
│ 初始化
▼
void someFunc(Widget w) parameter = w(永遠是 lvalue)
這個區分在 perfect forwarding 中最關鍵:把 argument 轉交給第二個函式時要保留其 lvalueness / rvalueness(詳見 Item 30 對應筆記)。
設計良好的函式應是 exception safe:
| 保證層級 | 例外拋出後的承諾 | 備註 |
|---|---|---|
| basic guarantee(基本保證) | 程式 invariant 完好(資料結構不毀損)、無資源洩漏 | exception safe 的最低要求 |
| strong guarantee(強保證) | 程式狀態回到呼叫前(如同交易 rollback) | 隱含 basic;與 Item 14 的 noexcept、move 策略密切相關 |
Function Object、Closure 與 Declaration/Definition
| 名詞 | 範圍 | 內容 |
|---|---|---|
| function object(狹義,C++98) | 最窄 | 支援 operator() 的物件 |
| function object(廣義,C++11) | 較寬 | 可用 名稱(引數) 語法呼叫者:含函式、C 式 function pointer |
| callable object | 最寬 | 廣義 function object 再加上 member function pointer |
| closure | — | lambda expression 在 runtime 產生的 function object |
實務上可將 function object / callable 統一想成「可用某種函式呼叫語法呼叫的東西」;lambda expression 與其產生的 closure 也很少需要區分(書中統稱 lambda)。同理,function template(產生函式的模板)與 template function(模板產生的函式)亦不刻意區分。
declaration 引入名稱與型別但不給細節;definition 提供儲存位置或實作。definition 同時也是 declaration:
extern int x; // 宣告:不配置儲存
class Widget; // 宣告:forward declaration
bool func(const Widget& w); // 宣告:無函式本體
enum class Color; // 宣告:scoped enum(見 Item 10 對應筆記)
int x; // 定義:配置儲存
class Widget { /* ... */ }; // 定義:完整類別
bool func(const Widget& w)
{ return w.size() < 10; } // 定義:有本體
enum class Color { Yellow, Red, Blue }; // 定義:列出 enumerator
signature(本書定義):宣告中指明參數型別與回傳型別的部分。上例 func 的 signature 是 bool(const Widget&)——函式名、參數名不算,noexcept、constexpr 等元素也排除在外。
Meyers 的 signature 定義是為本書方便而簡化;官方定義有時不含回傳型別,細節也不同。面試被問到時要能指出兩者差異。
版本沿革、Deprecated、UB 與指標用語
書中的版本稱呼有精確語意(C++98 與 C++03 僅技術細節不同,故合稱):
| 書中用語 | 實際涵蓋版本 | 範例敘述 |
|---|---|---|
| C++ | 所有版本 | C++ 重視效率 |
| C++98 | C++98 + C++03 | C++98 缺乏 concurrency 支援 |
| C++11 | C++11 + C++14(C++14 是 C++11 的 superset) | C++11 支援 lambda |
| C++14 | 僅 C++14 | C++14 支援一般化的函式回傳型別推導 |
- deprecated 特性:新標準通常保留舊 code 的有效性,但委員會可將特性標為 deprecated(「死刑候審」,未來可能移除)。編譯器不一定警告,仍應主動避免——deprecated 特性通常劣於取代它的新特性。經典例:
std::auto_ptr(C++11 deprecated)→ 一律改用std::unique_ptr。 - undefined behavior:標準宣告結果 undefined 時,runtime 行為完全不可預測。典型例:以
[]索引超出std::vector邊界、解參考未初始化的 iterator、data race(兩個以上 thread 同時存取同一記憶體位置且至少一方寫入)。 - raw pointer vs smart pointer:
new回傳的內建指標稱 raw pointer;smart pointer 通常 overloadoperator->與operator*。
std::weak_ptr 是例外——它不提供 operator* / operator-> 解參考(詳見 Item 20 對應筆記),必須先透過 lock() 取得 std::shared_ptr。
書中撰寫慣例速記:Widget = 任意 user-defined type;rhs/lhs = 右/左運算元(move、copy 操作與二元運算子的慣用參數名);… 窄省略號 = 「此處省略程式碼」,與 variadic template 的 ... 寬省略號不同;ctor/dtor = constructor / destructor 縮寫。
Exam/Test Patterns
| 情境關鍵字 | 答案 |
|---|---|
「Widget&& rhs 中的 rhs 是 lvalue 還是 rvalue?」 |
lvalue——所有 parameter 都是 lvalue,型別是 rvalue reference 不改變這點 |
| 「如何快速判斷 expression 是 lvalue?」 | 能取位址(&)者通常是 lvalue(heuristic,非嚴格定義) |
| 「move constructor 建出來的物件叫什麼?」 | 仍叫 copy;C++ 不區分 copy-constructed / move-constructed copy |
| 「argument 和 parameter 差在哪?」 | argument 是呼叫端 expression(可 l/r value);parameter 是函式內具名變數(恆為 lvalue) |
| 「basic vs strong exception guarantee?」 | basic:invariant 完好 + 不漏資源;strong:狀態回到呼叫前 |
| 「function object、callable、closure 的關係?」 | callable ⊇ 廣義 function object ⊇ 支援 operator() 者;closure 是 lambda 產生的 function object |
| 「本書說 C++11 時包含 C++14 嗎?」 | 包含——C++14 是 C++11 的 superset;寫 C++14 才專指 C++14 |
「std::auto_ptr 該用嗎?」 |
不該——C++11 已 deprecated,改用 std::unique_ptr |
| 「data race 的定義?」 | ≥2 thread 同時存取同一記憶體位置且至少一個是 writer → undefined behavior |
「signature 包含 noexcept 嗎?」 |
不含——signature 只有參數型別與回傳型別(本書定義) |
Related Notes
- 01-Introduction/Practice-Introduction — 本章練習題
- 06-Move-Semantics-And-Perfect-Forwarding/01-Std-Move-And-Std-Forward — rvalue/lvalue 區分的直接應用:
std::move為何只是 cast - 04-Moving-To-Modern-Cpp/08-Noexcept — exception safety 保證與
noexcept宣告的關係 - 06-Move-Semantics-And-Perfect-Forwarding/08-Perfect-Forwarding-Failure-Cases — argument 的 lvalueness/rvalueness 保留(perfect forwarding)
- 05-Smart-Pointers/03-Weak-Ptr — smart pointer 解參考慣例的例外