面試陷阱題 (Exam Traps)

Purpose

本頁彙整《Effective Modern C++》各章最容易在面試與考題中答錯的陷阱。每個陷阱以摺疊 callout 呈現:先自我測試「我會不會踩」,再展開對照「陷阱是什麼、為何易錯、正確做法」,並連回對應筆記深入複習。

導論 (Introduction)

型別推導 (Deducing Types)

auto (auto)

; 以為推導出 bool

  • 陷阱std::vector<bool>::operator[] 回傳隱形 proxy std::vector<bool>::reference,auto 忠實推導出 proxy。
  • 為何易錯:proxy 內含指向暫時 vector 的指標,語句結束後懸空——之後使用即 undefined behavior,且 auto 本身「沒推導錯」。
  • 正確做法:explicitly typed initializer idiom:auto highPriority = static_cast<bool>(features(w)[5]);,保留 auto 的所有優點。
  • 顯式型別初始器慣用法

邁向現代 C++ (Moving to Modern C++)

智慧指標 (Smart Pointers)

Move 語意與完美轉發 (Move Semantics and Perfect Forwarding)

Lambda 運算式 (Lambda Expressions)

會複製 data member

  • 陷阱:捕獲只作用於 non-static 區域變數與參數;[=] 實際複製的是 this 指標,物件析構後 closure 持有懸空指標。
  • 為何易錯:lambda body 裡寫 divisor 看起來就像捕獲了成員,實際是 this->divisor;用智慧指標管理物件也防不了。
  • 正確做法:C++14 用 init capture [divisor = divisor];C++11 先複製到區域變數再捕獲。另記:static 變數不會被捕獲,[=] 下仍是 by-reference 行為。
  • 避免預設捕獲模式

兩個 pw 是同一個變數

  • 陷阱= 左側是 closure class 的新資料成員,右側是外部區域變數,分屬不同 scope。
  • 為何易錯:同名遮蔽讓兩者看起來是一體;且 move 之後外部 pw 為 moved-from 狀態,不可再依賴其值。
  • 正確做法:理解 init capture 的雙 scope 設計;lambda 本體內的 pw 一律指 closure 成員。
  • Init Capture 用法

(auto x){ normalize(x); } 以為能保留值類別

  • 陷阱:具名參數 x 是 lvalue,即使呼叫端傳 rvalue,normalize 也永遠收到 lvalue。
  • 為何易錯:lambda 內沒有模板參數 T 可寫 std::forward<T>,很多人因此放棄轉發。
  • 正確做法:參數宣告為 auto&&,轉發寫 std::forward<decltype(x)>(x)——正確性由 reference collapsing 保證。
  • decltype 與 auto&& 轉發

並行 API (The Concurrency API)

微調 (Tweaks)