noexcept
說明符 (C++11 起)
指定函式是否可能丟擲異常。
目錄 |
[編輯] 語法
noexcept
|
(1) | ||||||||
noexcept( 表示式) |
(2) | ||||||||
throw()
|
(3) | (C++17 中已棄用) (C++20 中移除) | |||||||
noexcept(true)
noexcept
的 (
始終是此形式的一部分(它絕不能開始初始化器)。表示式 | - | 型別為 bool 的上下文轉換常量表達式 |
[編輯] 解釋
noexcept-specification 不屬於函式型別的一部分(就像 動態異常規範 一樣),並且只能作為 lambda 宣告符 或頂級 函式宣告符 的一部分出現,用於宣告函式、變數、函式型別的非靜態資料成員、函式指標、函式引用或成員函式指標,以及在這些宣告中宣告引數或返回型別時,如果該引數或返回型別恰好是函式指標或引用。它不能出現在 typedef 或 類型別名 宣告中。 void f() noexcept; // the function f() does not throw void (*fp)() noexcept(false); // fp points to a function that may throw void g(void pfa() noexcept); // g takes a pointer to function that doesn't throw // typedef int (*pf)() noexcept; // error |
(C++17 前) |
noexcept-specification 是函式型別的一部分,可以作為任何 函式宣告符 的一部分出現。 |
(C++17 起) |
C++ 中的每個函式要麼是*不丟擲*的,要麼是*可能丟擲*的
- *可能丟擲*的函式是
|
(C++17 前) |
- 聲明瞭
noexcept
說明符且其 表示式 求值為false
的函式 - 未宣告
noexcept
說明符的函式,但以下情況除外:
- 聲明瞭
|
(C++20 起) |
- 不丟擲函式是所有其他函式(那些帶有 noexcept 說明符且其 表示式 求值為
true
的函式,以及解構函式、預設的特殊成員函式和解分配函式)
顯式例項化可以使用 noexcept 說明符,但不是必需的。如果使用,異常規範必須與其他所有宣告相同。僅當在單個翻譯單元中異常規範不相同時才需要診斷。
僅在異常規範上有所不同的函式不能過載 (就像返回型別一樣,異常規範是函式型別的一部分,而不是函式簽名的一部分)(C++17 起)。
void f() noexcept; void f(); // error: different exception specification void g() noexcept(false); void g(); // ok, both declarations for g are potentially-throwing
指向不丟擲函式的指標(包括成員函式指標)可以賦值給或用於初始化(C++17 前)可 隱式轉換為(C++17 起) 指向可能丟擲函式的指標,但反之則不行。
void ft(); // potentially-throwing void (*fn)() noexcept = ft; // error
如果一個虛擬函式是不丟擲異常的,那麼其所有宣告,包括每個重寫器的定義,也必須是不丟擲異常的,除非重寫器被定義為已刪除
struct B { virtual void f() noexcept; virtual void g(); virtual void h() noexcept = delete; }; struct D: B { void f(); // ill-formed: D::f is potentially-throwing, B::f is non-throwing void g() noexcept; // OK void h() = delete; // OK };
不丟擲函式允許呼叫可能丟擲函式。每當丟擲異常並且處理器的搜尋遇到不丟擲函式的最外層塊時,函式 std::terminate 被呼叫
extern void f(); // potentially-throwing void g() noexcept { f(); // valid, even if f throws throw 42; // valid, effectively a call to std::terminate }
函式模板特化版本的異常規範不會與函式宣告一起例項化;它只在*需要*時(如下所述)才例項化。
隱式宣告的特殊成員函式的異常規範也僅在需要時才評估(特別是,派生類成員函式的隱式宣告不需要例項化基類成員函式的異常規範)。
當函式模板特化版本的 noexcept-specification *需要*時,但尚未例項化,則會查詢依賴名稱,並例項化 表示式 中使用的所有模板,如同用於特化版本的宣告一樣。
在以下語境中,函式的 noexcept-specification 被認為是*必需的*
- 在表示式中,函式透過過載決議被選擇
- 該函式被 ODR 使用
- 該函式將被 ODR 使用,但出現在未求值的運算元中
template<class T> T f() noexcept(sizeof(T) < 4); int main() { decltype(f<void>()) *p; // f unevaluated, but noexcept-spec is needed // error because instantiation of the noexcept specification // calculates sizeof(void) }
- 需要此規範以與另一個函式宣告進行比較(例如,在虛擬函式重寫器或函式模板的顯式特化上)
- 在函式定義中
- 需要該規範,因為預設的特殊成員函式需要檢查它以確定其自身的異常規範(這僅在預設的特殊成員函式的規範本身被需要時發生)。
*可能丟擲表示式*的形式定義(用於確定解構函式、建構函式和賦值運算子的預設異常規範,如上所述)
表示式 e
是*可能丟擲*的,如果
-
e
是對函式、函式指標或成員函式指標的函式呼叫,且該函式是*可能丟擲*的,除非e
是 核心常量表達式(C++17 前) -
e
隱式呼叫了*可能丟擲*的函式(例如過載運算子、new
表示式中的分配函式、函式引數的建構函式,或者如果e
是完整表示式,則為解構函式) -
e
是一個throw
-表示式 -
e
是一個轉換多型引用型別的dynamic_cast
-
e
是應用於解引用指向多型型別的指標的typeid
表示式 -
e
具有一個直接的子表示式,該子表示式是可能丟擲異常的
struct A { A(int = (A(5), 0)) noexcept; A(const A&) noexcept; A(A&&) noexcept; ~A(); }; struct B { B() throw(); B(const B&) = default; // implicit exception specification is noexcept(true) B(B&&, int = (throw Y(), 0)) noexcept; ~B() noexcept(false); }; int n = 7; struct D : public A, public B { int * p = new int[n]; // D::D() potentially-throwing because of the new operator // D::D(const D&) non-throwing // D::D(D&&) potentially-throwing: the default argument for B’s constructor may throw // D::~D() potentially-throwing // note; if A::~A() were virtual, this program would be ill-formed because an overrider // of a non-throwing virtual cannot be potentially-throwing };
[編輯] 注意
常量 表示式 的用途之一(與 noexcept
運算子 一起)是定義函式模板,這些模板為某些型別宣告 noexcept
而非其他型別。
請注意,函式上的 noexcept
規範不是編譯時檢查;它只是程式設計師通知編譯器函式是否應該丟擲異常的一種方法。編譯器可以使用此資訊對不丟擲函式啟用某些最佳化,並啟用 noexcept
運算子,該運算子可以在編譯時檢查特定表示式是否宣告丟擲任何異常。例如,像 std::vector 這樣的容器,如果其元素的移動建構函式是 noexcept
,則會移動其元素,否則會複製(除非複製建構函式不可訪問,但可能丟擲異常的移動建構函式是,在這種情況下,強異常保證被放棄)。
[編輯] 廢棄
noexcept
是 throw() 的改進版本,後者在 C++11 中已被廢棄。與 C++17 之前的 throw() 不同,noexcept
不會呼叫 std::unexpected,可能(也可能不)展開棧,並會呼叫 std::terminate,這可能允許編譯器實現 noexcept
而無需 throw() 的執行時開銷。從 C++17 開始,throw() 被重新定義為 noexcept(true) 的精確等價物。
功能測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__cpp_noexcept_function_type |
201510L |
(C++17) | 使異常規範成為型別系統的一部分 |
[編輯] 關鍵詞
noexcept, throw(C++17 起)(C++20 前)
[編輯] 示例
// whether foo is declared noexcept depends on if the expression // T() will throw any exceptions template<class T> void foo() noexcept(noexcept(T())) {} void bar() noexcept(true) {} void baz() noexcept { throw 42; } // noexcept is the same as noexcept(true) int main() { foo<int>(); // noexcept(noexcept(int())) => noexcept(true), so this is fine bar(); // fine baz(); // compiles, but at runtime this calls std::terminate }
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 1330 | C++11 | 異常規範可能被急切例項化 | 它僅在需要時才例項化 |
CWG 1740 | C++11 | 緊跟 noexcept 的 ( 可能開始一個初始化器 | 它只能是 noexcept 規範的一部分 |
CWG 2039 | C++11 | 只需要轉換前的表示式是常量 | 轉換也必須 在常量表達式中有效 |
[編輯] 參閱
noexcept 運算子(C++11) |
確定表示式是否丟擲任何異常 |
動態異常規範(C++17 前) | 指定函式丟擲哪些異常 (C++11 中廢棄) |
throw 表示式
|
發出錯誤訊號並將控制轉移給錯誤處理器 |
(C++11) |
如果移動建構函式不丟擲異常,則將引數轉換為亡值 (函式模板) |