名稱空間
變體
操作

noexcept 說明符 (C++11 起)

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
 
異常
try
丟擲異常
處理異常
異常規範
    noexcept 規範 (C++11)
    動態規範 (直到 C++17*)
noexcept 運算子 (C++11)
 

指定函式是否可能丟擲異常。

目錄

[編輯] 語法

noexcept (1)
noexcept(表示式) (2)
throw() (3) (C++17 中已棄用)
(C++20 中移除)
1) 等同於 noexcept(true)
2) 如果 表示式 求值為 true,則函式宣告為不丟擲任何異常。緊隨 noexcept( 始終是此形式的一部分(它絕不能開始初始化器)。
3) 等同於 noexcept(true)(參閱 C++17 之前的 動態異常規範 的語義)
表示式 - 型別為 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,則會移動其元素,否則會複製(除非複製建構函式不可訪問,但可能丟擲異常的移動建構函式是,在這種情況下,強異常保證被放棄)。

[編輯] 廢棄

noexceptthrow() 的改進版本,後者在 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 表示式 發出錯誤訊號並將控制轉移給錯誤處理器[編輯]
如果移動建構函式不丟擲異常,則將引數轉換為亡值
(函式模板) [編輯]