名稱空間
變體
操作

動態異常說明 (C++17 前)

來自 cppreference.com
< cpp‎ | 語言
 
 
C++ 語言
通用主題
流程控制
條件執行語句
if
迭代語句(迴圈)
跳轉語句
函式
函式宣告
Lambda 函式表示式
inline 說明符
動態異常說明 (C++17 前*)
noexcept 說明符 (C++11)
異常
名稱空間
型別
說明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
儲存期說明符
初始化
 
異常
try
丟擲異常
處理異常
異常規範
    noexcept 規範 (C++11)
    動態說明 (C++17 前*)
noexcept 運算子 (C++11)
 

列出函式可能直接或間接丟擲的異常。

目錄

[編輯] 語法

throw(型別標識列表 (可選)) (1) (在 C++11 中棄用)
(在 C++17 中已移除)
1) 顯式動態異常說明。
型別標識列表 - 逗號分隔的型別標識列表,表示包展開的型別標識後隨省略號 (...)(C++11 起)

顯式動態異常說明只能出現於函式型別、函式指標型別、函式引用型別或成員函式指標型別的函式宣告符上,且該型別是宣告或定義的頂層型別,或者出現於作為函式宣告符中形參或返回型別的這種型別上。

void f() throw(int);            // OK: function declaration
void (*pf)() throw (int);       // OK: pointer to function declaration
void g(void pfa() throw(int));  // OK: pointer to function parameter declaration
typedef int (*pf)() throw(int); // Error: typedef declaration

[編輯] 解釋

若函式宣告的動態異常說明中列出了型別 T,則該函式可丟擲該型別或派生自該型別的型別的異常。

異常說明中不允許出現不完整型別、除 cv void* 之外的不完整型別的指標或引用,以及右值引用型別(C++11 起)。若使用陣列和函式型別,則會調整為對應的指標型別,頂層 cv 限定符也會被丟棄。允許形參包(C++11 起)

調整後型別集合為空(在任何包展開後)(C++11 起)的動態異常說明是“不丟擲”的。帶有不丟擲動態異常說明的函式不允許任何異常。

動態異常說明不被認為是函式型別的一部分。

若函式丟擲其異常說明中未列出的型別的異常,則呼叫函式 std::unexpected。預設函式呼叫 std::terminate,但它可被使用者提供的函式替換(透過 std::set_unexpected),該函式可以呼叫 std::terminate 或丟擲異常。若從 std::unexpected 丟擲的異常被異常說明所接受,則棧回溯照常進行。若不接受,但異常說明允許 std::bad_exception,則丟擲 std::bad_exception。否則,呼叫 std::terminate

[編輯] 例項化

函式模板特化的動態異常說明不會與函式宣告一同例項化;它只在需要時(定義如下)才被例項化。

隱式宣告的特殊成員函式的動態異常說明也只在需要時才被求值(特別地,派生類成員函式的隱式宣告不要求基類成員函式的異常說明被例項化)。

需要函式模板特化的動態異常說明,但尚未例項化時,會查詢待決名,並且表示式中使用的任何模板都會被例項化,如同為該特化的宣告所做的一樣。

在下列語境中,函式的動態異常說明被認為是需要的

  • 在表示式中,函式被過載決議選中
  • 該函式被 ODR 使用
  • 該函式本會被 ODR 使用,但它出現在未求值運算元中
template<class T>
T f() throw(std::array<char, sizeof(T)>);
 
int main()
{
    decltype(f<void>()) *p; // f unevaluated, but exception specification is needed
                            // error because instantiation of the exception specification
                            // calculates sizeof(void)
}
  • 需要該說明來與另一個函式宣告進行比較(例如在虛擬函式覆蓋或函式模板的顯式特化上)
  • 在函式定義中
  • 需要該說明是因為一個被預設化的特殊成員函式需要檢查它以決定其自身的異常說明(這僅當該被預設化的特殊成員函式的說明本身是需要的時候發生)。

[編輯] 潛在異常

每個函式 f、函式指標 pf 和成員函式指標 pmf 都有一個潛在異常集合,由可能被丟擲的型別組成。所有型別的集合表示任何異常都可能被丟擲。此集合定義如下:

1)fpfpmf 的宣告使用了動態異常說明且該說明不允許所有異常(C++11 前),則該集合由該說明中列出的型別組成。
2) 否則,若 fpfpmf 的宣告使用了 noexcept(true),則該集合為空。
(C++11 起)
3) 否則,該集合是所有型別的集合。

注意:對於隱式宣告的特殊成員函式(建構函式、賦值運算子和解構函式)以及繼承建構函式(C++11 起),潛在異常的集合是它們將呼叫的所有東西的潛在異常集合的組合:非變體非靜態資料成員、直接基類,以及在適當情況下,虛基類的建構函式/賦值運算子/解構函式(一如既往,包括預設實參表示式)。

每個表示式 e 都有一個潛在異常集合。若 e 是一個核心常量表達式,則該集合為空,否則,它是 e 的所有直接子表示式(包括預設實參表示式)的潛在異常集合的並集,再與另一個取決於 e 形式的集合組合,如下所示:

1)e 是一個函式呼叫表示式,令 g 表示被呼叫的函式、函式指標或成員函式指標,則
  • g 的宣告使用了動態異常說明,則將 g 的潛在異常集合新增到該集合中;
(C++11 起)
  • 否則,該集合是所有型別的集合。
2)e 隱式呼叫了一個函式(它是一個運算子表示式且運算子被過載,它是一個 new 表示式且分配函式被過載,或者它是一個完整表示式且臨時物件的解構函式被呼叫),則該集合是該函式的集合。
3)e 是一個 throw 表示式,則該集合是由其運算元初始化的異常,或者對於重新丟擲的 throw 表示式(無運算元),是所有型別的集合。
4)e 是對多型型別的引用進行的 dynamic_cast,則該集合由 std::bad_cast 組成。
5)e 是應用於解引用的多型型別指標的 typeid,則該集合由 std::bad_typeid 組成。
6)e 是一個帶有非常量陣列大小的 new 表示式,且所選分配函式的潛在異常集合非空,則該集合由 std::bad_array_new_length 組成。
(C++11 起)
void f() throw(int); // f()'s set is "int"
void g();            // g()'s set is the set of all types
 
struct A { A(); };                  // "new A"'s set is the set of all types
struct B { B() noexcept; };         // "B()"'s set is empty
struct D() { D() throw (double); }; // new D's set is the set of all types

所有隱式宣告的成員函式和繼承建構函式(C++11 起)都有異常說明,選擇如下:

  • 若潛在異常的集合是所有型別的集合,則隱式異常說明允許所有異常(該異常說明被認為是存在的,即使它在程式碼中無法表達並且行為如同沒有異常說明一樣)(C++11 前)noexcept(false)(C++11 起)
  • 否則,若潛在異常的集合不為空,則隱式異常說明列出來自該集合的每種型別。
  • 否則,隱式異常說明是throw()(C++11 前)noexcept(true)(C++11 起)
struct A
{
    A(int = (A(5), 0)) noexcept;
    A(const A&) throw();
    A(A&&) throw();
    ~A() throw(X);
};
 
struct B
{
    B() throw();
    B(const B&) = default; // exception specification is "noexcept(true)"
    B(B&&, int = (throw Y(), 0)) noexcept;
    ~B() throw(Y);
};
 
int n = 7;
struct D : public A, public B
{
    // May throw an exception of a type that would match a handler of type
    // std​::​bad_array_new_length, but does not throw a bad allocation exception
    (void*) new (std::nothrow) int[n];
 
    // D may have the following implicitly-declared members:
    // D::D() throw(X, std::bad_array_new_length);
    // D::D(const D&) noexcept(true);
    // D::D(D&&) throw(Y);
    // D::~D() throw(X, Y);
};

[編輯] 注意

Clang 認為動態異常說明的例項化規則在 C++11 中由 CWG1330 更改,見 LLVM #56349

[編輯] 關鍵詞

throw

[編輯] 示例

注意:最好在 C++98 模式下編譯以避免警告。與 C++17 及更新版本不相容。

#include <cstdlib>
#include <exception>
#include <iostream>
 
class X {};
class Y {};
class Z : public X {};
class W {};
 
void f() throw(X, Y) 
{
    bool n = false;
 
    if (n)
        throw X(); // OK, would call std::terminate()
    if (n)
        throw Z(); // also OK
 
    throw W(); // will call std::unexpected()
}
 
void handler()
{
    std::cerr << "That was unexpected!\n"; // flush needed
    std::abort();
}
 
int main()
{
    std::set_unexpected(handler);
    f();
}

輸出

That was unexpected!

[編輯] 缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。

缺陷報告 應用於 釋出時的行為 正確的行為
CWG 25 C++98 賦值和初始化的行為
在具有不同異常說明的成員指標之間
是未指明的
應用限制
對於函式指標
和引用
CWG 973 C++98 異常說明可能包含函式型別,但
相應的函式指標轉換未被指明
已指定
CWG 1330 C++98 異常說明可能會被及早例項化 它只在需要時才被例項化
CWG 1267 C++11 異常說明中允許右值引用型別 不允許
CWG 1351 C++98
C++11
預設實參 (C++98) 和預設成員初始化器
(C++11) 在隱式異常說明中被忽略
改為被考慮
CWG 1777 C++11 throw(T...) 不是一個不丟擲的
說明,即使 T 是一個空包
它是不丟擲的
如果包為空
CWG 2191 C++98 typeid 表示式的潛在異常集合
可能包含 bad_typeid,即使它不能被丟擲
包含 bad_typeid
僅當它能被丟擲時

[編輯] 參閱

noexcept 說明符(C++11) 指明函式是否可能丟擲異常[編輯]