速查表 (Quick Reference)
全書 42 個 Item 的核心規則速查。每章一節:先列該章的關鍵公式/判斷表,再以「概念 | 規則 | 筆記連結」整理所有速查規則。詳細推導與陷阱請點入各筆記;面試陷阱另見 面試陷阱題,學習路線見 學習地圖。
導論 → 術語與慣例
| 概念 | 規則 | 筆記連結 |
|---|---|---|
| Parameter 恆為 lvalue | 所有函式 parameter 都是 lvalue,即使型別是 rvalue reference(如 Widget&& rhs);能取位址的 expression 通常是 lvalue |
術語與慣例 |
| Copy 用語 | 以同型別物件初始化的新物件一律稱 copy,不論經 copy 或 move construction 建立 | 術語與慣例 |
| Exception safety 兩級保證 | basic:例外後 invariant 完好、無資源洩漏;strong:例外後狀態回到呼叫前 | 術語與慣例 |
| 書中版本用語 | 書中「C++11」涵蓋 C++11 與 C++14;「C++98」涵蓋 C++98/03;寫「C++14」才專指 C++14 | 術語與慣例 |
第 1 章:型別推導 → 模板型別推導
Template Type Deduction 三情境表
| Case | ParamType 形式 | 推導規則 |
|---|---|---|
| Case 1 | T&、const T&、T* |
忽略 expr 的 reference-ness 後模式比對求 T;constness 保留 |
| Case 2 | T&&(universal reference) |
lvalue 引數 → T 與 param 皆推導為 lvalue reference(T 唯一為參考的情境);rvalue 引數 → 套用 Case 1 |
| Case 3 | T(pass-by-value) |
剝除 reference-ness 與頂層 const/volatile(底層 const 保留) |
)。
auto 與 template 推導差異(braced-init-list)
| 情境 | { 27 } 的推導結果 |
|---|---|
auto x = { 27 }; |
std::initializer_list<int> |
template<typename T> void f(T); 傳 { 27 } |
編譯失敗(non-deduced context)——這是兩者唯一差異 |
函式 auto 回傳型別 / generic lambda 的 auto 參數 |
實際採 template 推導,return { 1, 2, 3 }; 無法編譯 |
decltype 規則
| 對象 | 結果 |
|---|---|
| 名稱(name) | 如實回報宣告型別,reference 與 const 完整保留 |
| 比名稱複雜的 lvalue 運算式(型別 T) | 一律回報 T&——decltype(x) 是 int、decltype((x)) 是 int& |
decltype(auto)(C++14) |
像 auto 從 initializer 推導,但改用 decltype 規則,保留 reference 與 const |
| 概念 | 規則 | 筆記連結 |
|---|---|---|
| Case 1:參考/指標參數 | 忽略 expr 的 reference-ness 後模式比對求 T;constness 保留 | 模板型別推導 |
| Case 2:universal reference | lvalue → T 與 param 皆為 lvalue reference;rvalue → 套 Case 1 | 模板型別推導 |
| Case 3:pass-by-value | 剝除 reference-ness 與頂層 const/volatile——param 是全新副本 | 模板型別推導 |
| Array/Function decay | 陣列與函式名稱 by-value 傳入時 decay 成指標;T& 參數則保留完整型別 |
模板型別推導 |
| auto 推導本質 | auto 推導=template 推導:auto 扮演 T、type specifier 扮演 ParamType,三 Case 沿用 | auto 型別推導 |
| braced initializer 特例 | auto x = { 27 }; → std::initializer_list<int>;同樣的 {} 給 template 直接編譯失敗 |
auto 型別推導 |
| 回傳型別與 lambda 參數 | 函式 auto 回傳型別與 generic lambda 的 auto 參數採 template 推導,return {1,2,3}; 無法編譯 |
auto 型別推導 |
| auto&& | auto&& 是 universal reference:lvalue 初始化 → lvalue reference;rvalue → rvalue reference |
auto 型別推導 |
| decltype 基本行為 | 對名稱如實回報宣告型別,reference 與 const 完整保留 | decltype 規則 |
| lvalue expression 規則 | 對非名稱的 lvalue 運算式(型別 T)一律回報 T&;decltype((x)) 是 int& |
decltype 規則 |
| decltype(auto) | C++14:以 decltype 規則推導回傳型別/變數,回傳要與 c[i] 完全一致時使用;C++11 用 trailing return type |
decltype 規則 |
| auto 回傳型別陷阱 | 只寫 auto 作回傳型別採 template 推導、剝掉 reference,int& 變 rvalue int |
decltype 規則 |
| 編譯期檢視型別 | 宣告未定義模板 TD<decltype(x)> xType; 觸發編譯錯誤,錯誤訊息必含完整推導型別 |
檢視推導型別 |
| typeid 的失真 | std::type_info::name 依規格以 by-value 規則處理:剝 reference 再剝頂層 cv,必然失真 |
檢視推導型別 |
| 執行期準確輸出 | 用 Boost.TypeIndex 的 type_id_with_cvr<decltype(x)>().pretty_name(),保留 cvr 修飾 |
檢視推導型別 |
| 工具定位 | IDE/編譯錯誤/Boost.TypeIndex 都只是輔助;理解 Items 1-3 推導規則無可取代 | 檢視推導型別 |
第 2 章:auto → 優先使用 auto
| 概念 | 規則 | 筆記連結 |
|---|---|---|
| auto 強制初始化 | auto 變數必須有 initializer,從語法上根絕未初始化變數 | 優先使用 auto |
| 持有 closure | 用 auto 持有 lambda closure:比 std::function 更小、更快、可 inline、不 heap 配置 |
優先使用 auto |
| 避免 type shortcuts | auto sz = v.size(); 取得精確 size_type,避免 unsigned 在 64-bit 平台截斷 |
優先使用 auto |
| range-for 遍歷 map | 用 const auto&——元素實為 pair<const Key, Value>,顯式寫錯型別每圈複製暫時物件 |
優先使用 auto |
| vector<bool> 下標 | operator[] 回傳 proxy 而非 bool&;auto 接住會複製 proxy,可能懸空 → UB |
顯式型別初始器慣用法 |
| 隱形 proxy 與 auto | 隱形 proxy(vector<bool>::reference、expression templates)不應被 auto 直接推導;顯性 proxy(智慧指標)相容 |
顯式型別初始器慣用法 |
| 顯式型別初始器慣用法 | 不放棄 auto,改寫 auto x = static_cast<T>(expr); 強迫推導出 T |
顯式型別初始器慣用法 |
| 刻意轉型的表達 | 刻意縮窄轉換(double→float/int)用 static_cast 明示意圖,優於隱式轉換 |
顯式型別初始器慣用法 |
第 3 章:邁向現代 C++ → 大括號初始化
() vs {} 決策表
| 情況 | 用法 | 結果 |
|---|---|---|
| 需禁止 narrowing、避免 most vexing parse | {} |
Widget w{}; 呼叫建構子;Widget w(); 宣告函式 |
類別有 std::initializer_list 建構子 |
小心 {} |
只要引數能轉成元素型別就強制匹配 initializer_list 版(連 copy/move 都可被劫持);需 narrowing 直接編譯錯誤、不回退 |
空大括號 Widget w{}; |
— | 呼叫 default constructor,非空 initializer_list;要空 list 寫 w({}) 或 w{{}} |
std::vector<int> v(10, 20) vs v{10, 20} |
— | () 得 10 個 20;{} 得 2 個元素 10、20 |
| 模板內建立物件(如 make 函式) | 預設 () |
{} 與 () 語意可能完全不同,模板作者無從得知呼叫者意圖 |
| 概念 | 規則 | 筆記連結 |
|---|---|---|
| {} 三大優點 | 適用範圍最廣、禁止 narrowing conversion、免疫 most vexing parse | 大括號初始化 |
| initializer_list 劫持 | 引數有任何辦法轉成元素型別時 {} 強制匹配 initializer_list 建構子;narrowing 直接編譯錯誤不回退 |
大括號初始化 |
| null pointer 表示法 | 一律用 nullptr;0 是 int、NULL 是實作定義整數型別,都沒有指標型別 |
nullptr 用法 |
| nullptr 的型別 | std::nullptr_t,可隱式轉換為所有 raw pointer 型別,不可視為整數 |
nullptr 用法 |
| 0/NULL 進 template | 被推導為整數型別而非指標,轉交指標參數即編譯錯誤;nullptr 推導為 nullptr_t 沒問題 | nullptr 用法 |
| typedef vs using | 一律優先 alias declaration(using X = ...):與 typedef 等價但可模板化為 alias template |
別名宣告 |
| 模板內巢狀 typedef | X<T>::type 是 dependent type 須加 typename;alias template 不需要也不允許 |
別名宣告 |
| C++14 type traits | 用 std::transformation_t<T>(如 std::remove_reference_t<T>)取代 typename std::...<T>::type |
別名宣告 |
| scoped enum 作用域 | unscoped enum 列舉子洩漏到外層 scope;enum class 列舉子需 Color::white 存取 |
scoped enum 規則 |
| scoped enum 轉換 | 無任何隱含轉換,轉整數/浮點必須 static_cast;unscoped enum 隱含轉整數再轉浮點 |
scoped enum 規則 |
| 前向宣告 | scoped enum 永遠可前向宣告;unscoped enum 須指定底層型別才可以 | scoped enum 規則 |
| 禁止複製 | C++11 一律 = delete 標記 copy 操作並放 public 區段,取代 private 不定義手法 |
deleted functions 用法 |
| deleted 適用範圍 | 任何函式都可 = delete:member、non-member 與 template instantiations |
deleted functions 用法 |
| = delete 與重載 | deleted 函式仍參與 overload resolution,被選中即編譯錯誤——可過濾隱含轉換 | deleted functions 用法 |
| override 宣告 | 所有覆寫 base virtual 函式的 derived 函式一律加 override,讓「想覆寫卻沒覆寫」變編譯錯誤 |
override 宣告 |
| 覆寫六大條件 | base virtual、同名、同參數型別、同 constness、回傳/例外規格相容、同 reference qualifier | override 宣告 |
| Reference qualifiers | 成員函式尾端 & / && 限定 *this 為 lvalue / rvalue,rvalue 版可 std::move 移出成員 |
override 宣告 |
| const_iterator 優先 | 不需修改元素就用 const_iterator;C++11 起用成員 cbegin()/cend() |
const_iterator 用法 |
| 泛型 begin/end | 最大化泛型程式碼用 non-member begin/end/cbegin,支援內建陣列;non-member cbegin 是 C++14 才有 |
const_iterator 用法 |
| noexcept 與介面 | noexcept 是函式介面的一部分,呼叫端會依賴;事後移除會破壞 client code | noexcept 宣告 |
| noexcept 與 move | 容器擴容「move if you can, copy if you must」:僅當 move ctor 為 noexcept 才以 move 取代 copy | noexcept 宣告 |
| 預設隱含 noexcept | destructor 與 operator delete 預設隱含 noexcept;多數函式 exception-neutral 不該宣告 | noexcept 宣告 |
| constexpr 物件 | 一定是 const 且以編譯期已知值初始化;const 不保證編譯期已知 | constexpr 用法 |
| constexpr 函式 | 引數全為編譯期常量 → 編譯期結果;含執行期值 → 普通函式,一份程式碼兩種模式 | constexpr 用法 |
| constexpr 介面承諾 | constexpr 是介面的一部分,事後移除會破壞依賴編譯期常量的 client code | constexpr 用法 |
| const 與執行緒安全 | const 成員函式預設會被多執行緒並行呼叫,必須 thread safe(mutable 成員可能寫入) | const 與執行緒安全 |
| atomic vs mutex 選擇 | 單一變數/記憶體位置用 std::atomic(通常較便宜);兩個以上變數作為單位操作用 std::mutex |
const 與執行緒安全 |
| mutable 同步成員 | std::mutex 須宣告 mutable 才能在 const 成員函式內 lock/unlock;兩者皆 move-only |
const 與執行緒安全 |
| Move 生成三條件 | move 操作只在類別未宣告任何 copy 操作、move 操作、destructor 三者時才自動生成 | 特殊成員函式生成 |
| 宣告 move 對 copy 影響 | 宣告任一 move 操作 → 兩個 copy 操作被 =delete;宣告 copy → move 不生成 |
特殊成員函式生成 |
| Destructor 副作用 | 宣告 destructor 抑制 move 生成(copy 仍生成但已 deprecated);多型基底用 virtual ~Base() = default 加四個 = default |
特殊成員函式生成 |
| 成員模板不抑制生成 | member function template 永不抑制特殊成員函式生成,即使可實例化出相同簽章 | 特殊成員函式生成 |
第 4 章:智慧指標 → unique_ptr
Smart Pointer 選型表
| 需求 | 選擇 | 成本 |
|---|---|---|
| 專屬所有權(預設首選、工廠回傳) | std::unique_ptr |
預設 deleter 下與 raw pointer 同大小同指令;move-only |
| 共享所有權 | std::shared_ptr |
raw pointer 的 2 倍大(object ptr + control block ptr),計數為 atomic 操作 |
| shared_ptr-like 但可能懸空(cache、observer、打破 cycle) | std::weak_ptr |
與 shared_ptr 同大小、共用 control block、操作 weak count |
make 函式優缺點
| 面向 | 內容 |
|---|---|
| 優點 | 消除型別重複(DRY)、提升 exception safety;make_shared 單次配置更小更快 |
| 不能用 | 需要 custom deleter 或 braced initializer {} 時只能直接 new |
| shared_ptr 不宜 | 類別自訂 operator new/delete;大物件搭配長壽 weak_ptr(整塊記憶體要等 weak count 歸零才釋放) |
| 概念 | 規則 | 筆記連結 |
|---|---|---|
| 預設智慧指標選擇 | 優先 std::unique_ptr:預設 deleter 下與 raw pointer 同大小同指令,體現 exclusive ownership |
unique_ptr 專屬所有權 |
| move-only 語意 | unique_ptr 不可複製;move 轉移所有權且來源設為 null | unique_ptr 專屬所有權 |
| custom deleter 大小 | deleter 是型別的第二個模板引數;無捕獲 lambda 零開銷(首選),function pointer 增為兩個 word | unique_ptr 專屬所有權 |
| 工廠函式回傳型別 | 回傳 unique_ptr:呼叫者可輕鬆升級為 shared_ptr,反向轉換不存在 | unique_ptr 專屬所有權 |
| shared_ptr 成本 | 2 倍大小、reference count 增減為 atomic 操作;move 不動計數、比 copy 快 | shared_ptr 共享所有權 |
| Control block 建立規則 | make_shared、從 unique_ptr 或 raw pointer 建構 → 新 control block;從 shared_ptr/weak_ptr 建構 → 沿用 | shared_ptr 共享所有權 |
| Custom deleter 設計 | deleter 型別不屬於 shared_ptr 型別(存於 control block),不同 deleter 的 shared_ptr<T> 仍同型別 |
shared_ptr 共享所有權 |
| Raw pointer 禁忌 | 避免用 raw pointer 變數建構 shared_ptr(重複 control block → 重複 delete → UB);必要時直接傳 new 結果 | shared_ptr 共享所有權 |
| weak_ptr 本質 | shared_ptr 的 augmentation:不能 dereference、不能測 null,必須先轉回 shared_ptr | weak_ptr 用法 |
| 安全存取 | 先 expired() 再取值有 race condition;一律用原子操作 lock() 或 shared_ptr 建構子 |
weak_ptr 用法 |
| 三大使用場景 | caching factory、observer lists、打破 shared_ptr cycles(回指用 weak_ptr) | weak_ptr 用法 |
| make 三大優勢 | 消除型別重複、例外安全;make_shared/allocate_shared 另有單次配置 | make 函式 |
| 例外安全呼叫 | 禁止 fshared_ptr<W>(new W), g())——g( 可插在中間拋例外造成 leak;用 make 或獨立語句 |
make 函式 |
| make 不能用的情況 | custom deleter 或 braced initializer 時只能直接 new | make 函式 |
| make 轉發語意 | make 函式內部用 () 完美轉發:make_unique<vector<int>>(10, 20) 得 10 個 20,非 {10, 20} |
make 函式 |
| Pimpl 特殊成員函式位置 | unique_ptr pImpl 的 dtor/move/copy 一律「header 宣告、實作檔定義」(incomplete type 限制),= default 也要寫在實作檔 |
Pimpl Idiom 用法 |
| Pimpl copy 支援 | copy 必須手寫深拷貝:copy ctor 用 make_unique<Impl>(*rhs.pImpl)、copy assign 用 *pImpl = *rhs.pImpl |
Pimpl Idiom 用法 |
| unique vs shared deleter | unique_ptr 的 deleter 屬於型別(需 complete type);shared_ptr 的不屬於(Pimpl 儀式全免,但語意上獨占擁有仍應用 unique_ptr) | Pimpl Idiom 用法 |
第 5 章:Move 語意與完美轉發 → std::move 與 std::forward
Universal Reference 成立條件
型別推導發生(template 參數或
auto&&)+ 宣告形式精確為 type&&——兩者缺一不可,否則是 rvalue reference。std::vector<T>&&、const T&&、實體化後才呼叫的 push_back(T&&) 都不是 universal reference。Reference Collapsing 四規則
| 左 | 右 | 塌縮結果 |
|---|---|---|
& |
& |
& |
& |
&& |
& |
&& |
& |
& |
&& |
&& |
&&(唯一產生 rvalue reference 的組合) |
只發生在四種 context:template instantiation、auto 型別生成、typedef/alias declaration、decltype。
std::move / std::forward 使用時機
| 參數種類 | 工具 | 時機 |
|---|---|---|
| rvalue reference | std::move(無條件轉型) |
最後一次使用時 |
| universal reference | std::forward<T>(有條件轉型) |
最後一次使用時 |
| by-value 回傳綁在上述參考的參數 | return std::move(lhs) / return std::forward<T>(frac) |
copy 變 move |
| 符合 RVO 資格的區域變數 | 直接 return w;,勿加 std::move |
加了反而阻礙 copy elision |
| 概念 | 規則 | 筆記連結 |
|---|---|---|
| std::move 本質 | 無條件 rvalue cast,本身不搬移任何東西,執行期零成本 | std::move 與 std::forward |
| std::forward 本質 | 條件式轉型:僅當引數當初以 rvalue 初始化才轉為 rvalue,值類別編碼在模板參數 T | std::move 與 std::forward |
| const 與 move | 想被搬移的物件不要宣告 const:const rvalue 綁不上 move ctor,靜默退化為 copy | std::move 與 std::forward |
| 參數的值類別 | 具名函式參數永遠是 lvalue(即使型別是 Widget&&),轉交前必須 move/forward |
std::move 與 std::forward |
| UR 成立條件 | 型別推導+形式精確為 type&& 缺一不可;否則是 rvalue reference |
universal reference 判定 |
| 初始器決定身分 | UR 以 lvalue 初始化 → lvalue reference(T 為 Widget&);rvalue → rvalue reference(T 為 Widget) |
universal reference 判定 |
| 破格情形 | std::vector<T>&&、const T&&、實體化後才呼叫的 push_back(T&&) 都不是 UR |
universal reference 判定 |
| move/forward 配對 | rvalue reference → 無條件 std::move;universal reference → 有條件 std::forward,不可交換 | move 與 forward 使用時機 |
| 最後一次使用 | 多次使用同一參考時,只在最後一次套 move/forward,避免物件提前被搬空 | move 與 forward 使用時機 |
| RVO | 以值回傳區域變數直接 return w;;加 std::move 違反 RVO 條件,強制多一次 move |
move 與 forward 使用時機 |
| UR 重載禁忌 | 不要對 UR 參數做重載——它幾乎對任何型別產生 exact match,吸走預期外的呼叫 | 避免對 universal reference 重載 |
| Forwarding ctor vs copy ctor | 對 non-const lvalue,forwarding ctor 實例化出 Person(Person&) 比 copy ctor 更佳匹配 |
避免對 universal reference 重載 |
| 繼承 + forwarding ctor | derived 的 copy/move ctor 呼叫 base 會被 base 的 forwarding ctor 攔截 | 避免對 universal reference 重載 |
| 不需完美轉發時 | 優先改名(放棄重載)、pass by const T& 或 pass by value 取代 UR 重載 |
重載的替代方案 |
| Tag dispatch | 單一入口以 std::true_type/false_type 分派;判斷整數需先 std::remove_reference_t<T> |
重載的替代方案 |
| Constructor 場合 | tag dispatch 被生成的 copy/move ctor 繞過,須用 std::enable_if + !is_base_of<Person, decay_t<T>> |
重載的替代方案 |
| 塌縮規則 | 任一方為 & → 結果 &;只有 && + && → && |
reference collapsing 規則 |
| 四種 context | template instantiation、auto 型別生成、typedef/alias、decltype | reference collapsing 規則 |
| lvalue/rvalue 編碼 | 傳 lvalue 給 T&& → T 推導為 Widget&;傳 rvalue → T 為 Widget(非 reference)——std::forward 的基礎 |
reference collapsing 規則 |
| 泛型 move 假設 | 型別未知時假設 move not present, not cheap, not used;已知且穩定則查證後依賴 | move 操作的現實 |
| 容器 move 複雜度 | std::vector move O(1)(搬 heap 指標);std::array move O(n)(資料在物件內) |
move 操作的現實 |
| SSO 與短字串 | SSO 短字串存在物件內部緩衝區,move 沒有指標可搬,不比 copy 快 | move 操作的現實 |
| 完美轉發失敗模式 | 只有兩種:模板型別推導失敗(編譯錯誤),或推導出錯誤型別 | 完美轉發失敗案例 |
| 五大失敗案例 | braced initializers、0/NULL 空指標、僅宣告的整數 static const 成員、多載函式名/模板名、bitfields | 完美轉發失敗案例 |
| braced initializer 解法 | 先 auto il = {1,2,3};(推導為 initializer_list)再 fwd(il) |
完美轉發失敗案例 |
第 6 章:Lambda 運算式 → 避免預設捕獲模式
Lambda Capture 決策表
| 情境 | 正確寫法 |
|---|---|
| 任何捕獲 | 避免 [&] / [=],明確列出捕獲對象 |
| closure 可能存活超過區域變數 | 禁用 [&](dangling reference);明確 [&x] 至少點名依賴 |
| member function 內用 data member | [=] 捕的是 this 指標;C++14 用 init capture [divisor = divisor],C++11 先複製到區域變數 |
| move-only 物件進 closure | C++14 init capture [pw = std::move(pw)];C++11 用手寫 class 或 std::bind |
| static / global 變數 | 不可被捕獲;[=] 下 lambda 仍直接參照,closure 並非 self-contained |
| generic lambda 完美轉發 | auto&& 參數 + std::forward<decltype(x)>(x) |
| 概念 | 規則 | 筆記連結 |
|---|---|---|
| 預設 [&] 捕獲 | closure 存活超過區域變數即 dangling reference;明確寫 [&x] 才能點名依賴 |
避免預設捕獲模式 |
| 預設 [=] 與 this | member function 內 [=] 捕獲的是 this 指標而非 data member;物件亡則指標懸空 |
避免預設捕獲模式 |
| 捕獲 data member 正解 | C++11 先複製到區域變數再捕獲;C++14 用 init capture [divisor = divisor] |
避免預設捕獲模式 |
| Move 物件進 closure | C++14 init capture [pw = std::move(pw)];C++11 lambda 無原生 move capture |
init capture 用法 |
| Init capture 的 scope | = 左側是 closure class 資料成員名,右側運算式在 lambda 定義處的 scope 求值 |
init capture 用法 |
| C++11 模擬 move capture | 手寫 closure class,或 std::bind + lambda 以 lvalue reference(通常 const&)接收 |
init capture 用法 |
| Generic lambda 完美轉發 | lambda 內沒有 T 可用時,以 std::forward<decltype(param)>(param) 轉發 auto&& 參數 |
decltype 轉發 |
| decltype 對 UR 參數 | 傳入 lvalue → decltype 為 lvalue reference;rvalue → rvalue reference | decltype 轉發 |
| collapsing 保證正確性 | std::forward<Widget&&> 經 &&+&& 摺疊後與 std::forward<Widget> 實例化完全相同 |
decltype 轉發 |
| Lambda vs std::bind | 一律優先 lambda:更可讀、更有表達力、可能更快(易 inline);C++14 起 bind 無合理用例 | lambda 優於 std::bind |
| bind 引數求值時機 | 傳給 std::bind 的運算式當下立即求值存入 bind object;延遲求值須巢狀 bind | lambda 優於 std::bind |
| bind 儲存/傳遞規則 | 引數一律以值儲存(std::ref 才是參考);呼叫時引數一律以參考傳遞 | lambda 優於 std::bind |
第 7 章:並行 API → task-based 優先
std::async Launch Policy 表
| Policy | 行為 |
|---|---|
std::launch::async |
保證在另一條執行緒非同步執行 |
std::launch::deferred |
僅在對 future 呼叫 get/wait 時於呼叫者執行緒同步執行;從未呼叫則永不執行 |
預設(async | deferred 的 OR 組合) |
由執行期系統決定,不保證併發;wait_for 迴圈可能永不終止 |
| 偵測 deferred | fut.wait_for(0s) == std::future_status::deferred |
Thread / Future Destructor 行為表
| Handle | 解構行為 |
|---|---|
joinable std::thread |
std::terminate(程式終止) |
unjoinable std::thread(預設建構、被 move 走、已 join、已 detach) |
安全 |
| 一般 future | 只銷毀資料成員、遞減 shared state 參考計數;不 join、不 detach、不執行任務 |
來自 std::async + launch::async + 最後一個指涉的 future |
阻塞至任務完成(隱含 join),永不終止程式 |
| packaged_task / promise 建立的 future | 一律正常行為;join/detach 由操作底層 std::thread 的程式碼決定 |
atomic vs volatile 對照表
| 面向 | std::atomic |
volatile |
|---|---|---|
| 用途 | 多執行緒並行共享資料 | 特殊記憶體(memory-mapped I/O) |
| 原子性 | 所有成員函式(含 RMW)保證原子 | 無——同時讀寫是 data race → UB |
| 重排限制 | 預設 sequential consistency | 無 |
| redundant loads / dead stores | 可被優化 | 禁止優化 |
| 合用 | 特殊記憶體又被多執行緒存取:volatile std::atomic<T> |
同左 |
| 概念 | 規則 | 筆記連結 |
|---|---|---|
| 非同步預設寫法 | 優先 std::async(task-based)而非 std::thread;future 的 get() 可取回傳值與例外 |
task-based 優先 |
| std::thread 例外行為 | thread 上的函式丟例外 → std::terminate;建構時執行緒耗盡丟 std::system_error | task-based 優先 |
| 直接用 thread 的時機 | 僅限需要 native_handle()(priority/affinity)、自行最佳化 thread 用量、自建 thread pool | task-based 優先 |
| async 預設 policy | async | deferred OR 組合,由執行期系統決定,不保證併發 |
std::launch::async 指定 |
| deferred 執行時機 | 僅在 get/wait 時於呼叫者 thread 同步執行;從未呼叫則永不執行 | std::launch::async 指定 |
| 保證真正非同步 | 必須併發時明確傳 std::launch::async,或用完美轉發包裝的 reallyAsync |
std::launch::async 指定 |
| joinable thread 解構 | 直接 std::terminate;用 RAII 物件保證所有離開路徑上皆 unjoinable | thread unjoinable 原則 |
| unjoinable 四情況 | 預設建構、被 move 走、已 join、已 detach;run to completion 仍是 joinable | thread unjoinable 原則 |
| thread 成員宣告順序 | std::thread 資料成員宣告在類別最後——建構後可能立即執行 | thread unjoinable 原則 |
| future 解構正常行為 | 只銷毀資料成員並遞減 shared state 參考計數,不 join/detach/執行任務 | thread handle 解構 |
| 解構阻塞三條件 | shared state 來自 std::async + policy 為 launch::async + 最後一個指涉的 future → 阻塞至完成 | thread handle 解構 |
| thread vs future 對比 | joinable thread 解構 → terminate;future 解構永不終止程式,最多阻塞 | thread handle 解構 |
| 一次性事件通訊 | 用 std::promise<void> + std::future<void>:免 mutex、免 spurious wakeup、真正阻塞 |
void future 事件通訊 |
| condvar 方案缺陷 | 需多餘 mutex、notify 先於 wait 造成永久等待、需 lambda 防 spurious wakeup | void future 事件通訊 |
| 多個反應任務 | 用 future::share() 取得 shared_future,每條反應執行緒以傳值捕獲自己的拷貝 |
void future 事件通訊 |
| std::atomic 用途 | 多執行緒共享資料不用 mutex 時:所有成員函式原子、預設 sequential consistency 限制重排 | atomic 與 volatile |
| volatile 用途 | 專用於特殊記憶體:禁止優化 redundant loads 與 dead stores,與執行緒安全無關 | atomic 與 volatile |
| 多執行緒下的 volatile | 同時讀寫 volatile 是 data race → UB;兩執行緒各 ++ 一次 volatile int 可能得 1,atomic 必為 2 | atomic 與 volatile |
第 8 章:微調 → 值傳遞
Pass-by-Value 四條件
| # | 條件 |
|---|---|
| 1 | copyable(move-only 型別應寫單一 rvalue reference overload) |
| 2 | cheap to move(如 SSO 字串的 move 不便宜) |
| 3 | always copied(不會有條件地不複製) |
| 4 | 仍需個案評估(consider)——成本恆多一次 move:lvalue = 1 copy + 1 move;rvalue = 2 moves |
Emplacement 幾乎必快三條件
| # | 條件 |
|---|---|
| 1 | 值以建構(非指派)方式進入容器 |
| 2 | 引數型別異於容器元素型別 |
| 3 | 不會被容器當重複值拒絕 |
| 概念 | 規則 | 筆記連結 |
|---|---|---|
| Pass by value 適用條件 | copyable + cheap to move + always copied + 個案評估,四項全滿足才考慮 | 值傳遞 |
| 值傳遞成本 | 比 by-reference 恆多一次 move:lvalue = 1 copy + 1 move;rvalue = 2 moves | 值傳遞 |
| Slicing problem | base class 型別參數不可值傳遞——衍生物件會被切割成 base 部分 | 值傳遞 |
| Emplacement 三條件 | 以建構進容器+引數型別異於元素型別+不被當重複值拒絕 → 幾乎必快 | emplacement 用法 |
| push_back("literal") 成本 | 產生暫時 std::string:2 次建構+1 次解構;emplace_back 僅 1 次建構 | emplacement 用法 |
| explicit 建構子差異 | insertion 用 copy initialization(禁 explicit);emplacement 用 direct initialization(允許 explicit,可能放行錯誤程式碼) | emplacement 用法 |
| 資源管理物件入容器 | 先在獨立敘述建立 smart pointer,再 push_backmove(spw);勿把 new 直接丟給 emplace |
emplacement 用法 |
必背模式 → universal reference 判定
- T&& 不一定是 rvalue reference:型別推導發生+形式精確為
type&&才是 universal reference → universal reference - 具名參數永遠是 lvalue:rvalue reference 用
std::move、universal reference 用std::forward,都在最後一次使用時 → move 與 forward 使用時機 - Reference collapsing:任一方是
&結果就是&,只有&&+&&才是&&→ reference collapsing - auto 與 template 推導唯一差異:braced-init-list → auto 推導為
std::initializer_list、template 直接失敗 → auto 型別推導 - decltype(x) 是 int、decltype((x)) 是 int&:非名稱的 lvalue 運算式一律回報
T&→ decltype - {} 禁止 narrowing、免疫 most vexing parse,但 initializer_list 建構子會劫持重載決議 → 大括號初始化
- 智慧指標選型:預設 unique_ptr、共享才 shared_ptr、可能懸空用 weak_ptr → unique_ptr
- 優先 make_unique/make_shared;custom deleter 與 braced initializer 是唯二只能直接 new 的情況 → make 函式
- move 未宣告 noexcept 就不會被用:vector 擴容為保 strong guarantee 退回 copy → noexcept
- 宣告 destructor 或 copy 操作任一,move 就不自動生成(改用 copy,靜默變慢) → 特殊成員函式生成
- 避免 [=] / [&] 預設捕獲:member function 內
[=]捕的是 this 指標;C++14 用 init capture → 避免預設捕獲模式 - std::async 預設不保證非同步:必須併發時明確指定
std::launch::async→ std::launch::async