noexcept
说明符 (自 C++11 起)
指定函数是否可以抛出异常。
内容 |
[编辑] 语法
noexcept
|
(1) | ||||||||
noexcept( 表达式) |
(2) | ||||||||
throw()
|
(3) | (C++17 中已弃用) (C++20 中已删除) | |||||||
noexcept(true)
相同(
紧跟在 noexcept
后面始终是此形式的一部分(它永远不会开始初始化器)。表达式 | - | 上下文转换的常量表达式,类型为 bool |
[编辑] 说明
noexcept 规范不是函数类型的一部分(就像 动态异常规范 一样),并且只能在声明函数、变量、类型为函数的非静态数据成员、指向函数的指针、指向函数的引用或指向成员函数的指针时作为 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 规范是函数类型的一部分,可以作为任何 函数说明符 的一部分出现。 |
(自 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
说明需要但尚未实例化时,将查找依赖名称,并将noexcept
说明中使用的任何模板实例化为好像用于该特化的声明一样。
在以下情况下,函数的noexcept
说明被认为是需要的
- 在一个表达式中,其中该函数通过重载解析被选中
- 该函数被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 };
[edit] Notes
常量表达式的用途之一是(与noexcept
运算符一起)定义对某些类型声明为noexcept
,而对其他类型则不声明为noexcept
的函数模板。
请注意,函数上的noexcept
说明不是编译时检查;它仅仅是一种方法,供程序员告知编译器函数是否应该抛出异常。编译器可以使用此信息对非抛出异常函数启用某些优化,并启用noexcept
运算符,该运算符可以在编译时检查特定表达式是否声明为抛出任何异常。例如,像std::vector这样的容器将在元素的移动构造函数为noexcept
的情况下移动其元素,否则将复制(除非复制构造函数不可访问,但存在可能抛出异常的移动构造函数,在这种情况下,强异常保证将被放弃)。
[edit] Deprecates
noexcept
是throw()的改进版本,后者在 C++11 中已被弃用。与 C++17 之前的throw()不同,noexcept
不会调用std::unexpected,可能或可能不反转堆栈,并将调用std::terminate,这可能允许编译器实现noexcept
,而无需throw()的运行时开销。自 C++17 起,throw()被重新定义为与noexcept(true)完全相同。
特性测试宏 | 值 | Std | 特性 |
---|---|---|---|
__cpp_noexcept_function_type |
201510L | (C++17) | 使异常说明成为类型系统的一部分 |
[edit] Keywords
noexcept, throw(自 C++17 起)(直到 C++20)
[edit] Example
// 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 }
[edit] Defect reports
以下行为更改缺陷报告被追溯应用于之前发布的 C++ 标准。
DR | 应用于 | 发布的行为 | 正确行为 |
---|---|---|---|
CWG 1330 | C++11 | 异常说明可能被急切实例化 | 它只有在需要时才会实例化 |
CWG 1740 | C++11 | ( 紧跟在 noexcept 之后可能开始一个初始化器 |
它只能是noexcept 说明的一部分 |
CWG 2039 | C++11 | 只有转换之前的表达式需要是常量 | 转换也必须是 常量表达式中有效的 |
[edit] See also
noexcept 运算符(C++11) |
确定表达式是否抛出任何异常 |
动态异常说明(直到 C++17) | 指定函数抛出的异常(C++11 中已弃用) |
throw 表达式
|
发出错误信号并将控制权转移到错误处理程序 |
(C++11) |
如果移动构造函数不抛出异常,则将参数转换为右值引用 (函数模板) |