使用 noexcept 我們需要知道什麼?

noexcept 是什麼?

noexcept 是自 C++11 引入的新特性,指定函數是否可能會引發異常,以下是 noexcept 的標準語法:

noexcept-expression:
    noexcept
    noexcept(** *constant-expression* **)

constant-expression 是一個 bool 類型的常量表達式,是一種異常規範(exception specification),屬於C++的語言特性,表示是否會發生異常。

noexcept 等效於 noexcept(true)

noexcept(true) 或者 noexcept 表示函數不會拋出或者傳遞異常,如果函數發生異常,將調用 std::terminate 立即終止程式。

noexcept(false) or 或者不使用 noexcept (析構函數或釋放函數默認聲明為 noexcept), 表示函數所有可能的異常都會被拋出.

應該使用 nonexcept 的情形

建議將所有不會拋出異常(包括以後)的函數聲明為 noexcept
當函數聲明為 noexcept 後,編譯器能夠在一些不同的上下文環境中產生更加高效的程式碼。

不應該使用 nonexcept 的情形

函數可以標記為 noexcept 當且僅當內部調用的所有函數也都直接或者間接的標記為 noexcept 或者 const

編譯器沒有義務檢查所有層級程式碼是否會拋出異常到 noexcept 函數。
如果標記了 noexcept 的函數確實拋出了異常,那麼std::terminate將會被立即調用,並且不能保證函數內部的對象能夠被析構。

比起優化,正確性更為重要。

當你在最開始聲明一個函數為 noexcept, 而後又反悔想要去掉 noexcept 標記,那麼你將會影響到調用端的程式碼。

示例

下面的函數被標記為有條件的 noexcept:函數是否為 noexcept 取決於 noexcept 的子句表達式是否為 noexcept

例如,有兩個包含 Widget 對象的數組,交換兩個數組的函數是否為 noexcept 取決於 交換兩個數組中元素的函數是否為 noexcept,即,交換兩個 Widget 對象是否為 noexcept

因此 Widget 對象 swap 的實現決定了 Widget 數組的交換函數是否為 noexcept

同樣地,包含 Widgets 的 std::pair 對象的交換函數是否應該為 noexcept 取決於交換兩個 Widget 對象是否為 noexcept

上層的數據結構操作可以為 noexcept 僅當下層的數據結構操作為 noexcept。這就促使你,只要允許,就儘可能地提供 noexcept 的函數。

template <class T, size_t N>
void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a, *b))); 

template <class T1, class T2>
struct pair {
    …
    void swap(pair& p) noexcept(noexcept(swap(first, p.first)) && noexcept(swap(second, p.second)));
    …
};

總結

  • noexcept 是函數介面的一部分,這意味著調用者會依賴它。
  • noexcept 函數可優化性要高於 non-noexcept 函數。
  • noexcept 用在數據移動,交換,記憶體釋放函數,析構函數中會更有價值。
  • 大多數函數本質上是屬於 non-noexcept 的。

擴展

在 C++17 之前還有一種異常規範 (dynamic exception pecification) throw(optional_type_list)
C++17 之後 throw(optional_type_list) 已被移除(除了 throw()),throw() 等同於 noexcept

應該避免使用 throw(optional_type_list) 或者 throw()

Tags: