Dynamic exception specification (until C++17)
列出函数可能直接或间接抛出的异常。
目录 |
语法
throw(
类型标识列表
(可选)
)
|
(1) |
(C++11 中弃用)
(C++17 中移除) |
|||||||
| type-id-list | - | 以逗号分隔的 类型标识 列表 ,表示 包展开 的类型标识后接省略号 (...) (C++11 起) |
显式动态异常规范应仅出现在函数类型、指向函数类型的指针、引用函数类型或指向成员函数类型的指针的声明符上,这些类型必须是声明或定义的顶层类型,或者作为函数声明符中的参数或返回类型出现。
void f() throw(int); // 正确:函数声明 void (*pf)() throw (int); // 正确:函数指针声明 void g(void pfa() throw(int)); // 正确:函数指针参数声明 typedef int (*pf)() throw(int); // 错误:typedef声明
说明
如果函数在其动态异常规范中声明了类型
T
,则该函数可能抛出该类型或其派生类型的异常。
不完整类型
、指向不完整类型的指针或引用(cv
void*
除外)
以及右值引用类型
(C++11 起)
不允许出现在异常规范中。若使用数组和函数类型,会被调整为对应指针类型,同时顶层 cv 限定符也会被移除。
允许使用
形参包
(C++11 起)
。
动态异常规范当其调整类型集合为空时 (在任何包展开后) (since 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 未求值,但需要异常规范 // 错误:因为实例化异常规范时 // 需要计算 sizeof(void) }
- 需要规范来与另一个函数声明进行比较(例如,在虚函数重写或函数模板显式特化时)
- 在函数定义中
- 需要规范是因为默认的特殊成员函数需要检查它,以决定自身的异常规范(这仅在默认特殊成员函数本身的规范被需要时发生)。
潜在异常
每个函数
f
、函数指针
pf
以及成员函数指针
pmf
都具有一个
潜在异常集合
,该集合包含可能被抛出的类型。所有类型的集合表示可能抛出任何异常。该集合的定义如下:
f
、
pf
或
pmf
的声明使用了不允许所有异常的动态异常规范
that does not allow all exceptions
(until C++11)
,则该集合由该规范中列出的类型构成。
| (since C++11) |
注意:对于隐式声明的特殊成员函数(构造函数、赋值运算符和析构函数) 以及继承构造函数 (C++11 起) ,其潜在异常集合由它们所调用操作的全部潜在异常组合而成:包括非变体非静态数据成员、直接基类以及(若适用)虚基类的构造函数/赋值运算符/析构函数(一如既往地包含默认参数表达式)。
每个表达式
e
都有一个
潜在异常集合
。若
e
是
核心常量表达式
,则该集合为空;否则,该集合是
e
的所有直接子表达式(包括
默认参数表达式
)的潜在异常集合的并集,再与另一个取决于
e
形式的集合组合而成,具体如下:
e
是函数调用表达式,令
g
表示被调用的函数、函数指针或成员函数指针,则
-
-
若
g的声明使用动态异常规范,则将g的潜在异常集合加入当前集合;
-
若
|
(since C++11) |
-
- 否则,该集合为所有类型的集合。
| (C++11 起) |
void f() throw(int); // f() 的异常集合为 "int" void g(); // g() 的异常集合为所有类型的集合 struct A { A(); }; // "new A" 的异常集合为所有类型的集合 struct B { B() noexcept; }; // "B()" 的异常集合为空 struct D() { D() throw (double); }; // new D 的异常集合为所有类型的集合
所有隐式声明的成员函数 和继承构造函数 (since 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; // 异常规范为 "noexcept(true)" B(B&&, int = (throw Y(), 0)) noexcept; ~B() throw(Y); }; int n = 7; struct D : public A, public B { // 可能抛出与 std::bad_array_new_length 类型匹配的异常,但不会抛出内存分配异常 (void*) new (std::nothrow) int[n]; // D 可能包含以下隐式声明的成员: // 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 。
关键词
示例
注意:建议在 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)
|
指定函数是否可能抛出异常 |