命名空间
变体
操作

动态异常说明 (C++17 前)

来自 cppreference.cn
< 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) 指明函数是否可能抛出异常[编辑]