C++98 throw() 異常規範複雜、脆弱、編譯器難以優化。C++11 推出 noexcept:非黑即白——函式要麼可能拋出異常,要麼保證不拋出(noexcept)。
函式是否 noexcept 影響呼叫端的異常安全與效率決策。知道不拋異常卻不標記 noexcept,屬於介面設計缺失。
RetType func(params) noexcept; // 最大優化:無需維護堆疊展開資訊、析構順序
RetType func(params) throw(); // C++98 風格:無優化彈性
RetType func(params); // 可能拋出:無優化
noexcept 允許編譯器省略異常處理機制的設置/銷毀開銷。
std::vector::push_back 擴容時需將舊元素遷移至新記憶體。C++98 採用複製(強異常安全);C++11 想用移動(更快),但移動若拋出異常會破壞強異常安全(已移動元素無法回滾)。
策略:移動若 noexcept 則用移動,否則退回複製("move if you can, but copy if you must")。標準庫容器(vector、deque 等)通過 noexcept 檢測移動建構/指派是否安全。
標準庫 swap 是否 noexcept 取決於元素 swap 是否 noexcept:
template
void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a, *b))); // 陣列 swap
template
struct pair {
void swap(pair& p) noexcept(noexcept(swap(first, p.first)) && noexcept(swap(second, p.second)));
};
高層資料結構 swap noexcept 要求低層組件 swap noexcept,這激勵我們為自訂類型提供 noexcept swap。
noexcept 是介面承諾,違反導致 std::terminate。僅在確信長期不拋出異常時標記。swap、記憶體釋放函式、解構函式——天然適合 noexcept,收益顯著。operator delete 預設隱式 noexcept(除非成員明確 noexcept(false))。noexcept;Narrow contract(有前置條件,違反即 UB)標記需謹慎——若內部檢查前置條件並拋出異常,將與 noexcept 衝突。void setup(); // 文檔說不拋出,但未標記 noexcept
void cleanup();
void doWork() noexcept { setup(); ... cleanup(); } // 合法,編譯器不警告
合法場景存在(如 C 庫函式、未遷移的 C++98 代碼),故編譯器不強制驗證。
noexcept 是介面一部分,呼叫端可能依賴。noexcept 函式優化優於非 noexcept。swap、釋放函式、解構函式受益最大。