命名空间
变体
操作

noexcept 说明符 (C++11 起)

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
 
异常
try
抛出异常
处理异常
异常规范
    noexcept 规范 (C++11)
    动态规范 (C++11 前已弃用*)
noexcept 运算符 (C++11)
 

指定函数是否可能抛出异常。

目录

[编辑] 语法

noexcept (1)
noexcept(expression) (2)
throw() (3) (C++17 中已弃用)
(C++20 中已移除)
1)noexcept(true) 相同
2) 如果 expression 的值为 true,则该函数声明为不抛出任何异常。noexcept 后的 ( 始终是此形式的一部分(它永远不能启动初始化器)。
3)noexcept(true) 相同 (有关 C++17 之前的语义,请参见 动态异常规范)
expression - 上下文相关的转换到 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 说明符 声明且其 expression 的值为 false 的函数
  • 未使用 noexcept 说明符 声明的函数,但以下情况除外
  • 隐式构造函数定义将调用的基类或成员的构造函数是可能抛出的 (见下文)
  • 此类初始化的子表达式 (例如默认实参表达式) 是可能抛出的 (见下文)
  • 默认成员初始化器 (仅对于默认构造函数) 是可能抛出的 (见下文)
  • 复制赋值 运算符、移动赋值 运算符,这些运算符在其首次声明时是隐式声明或默认的,除非隐式定义中任何赋值运算符的调用是可能抛出的 (见下文)
  • 比较运算符,这些运算符在其首次声明时是默认的,除非隐式定义中任何比较运算符的调用是可能抛出的 (见下文)
(C++20 起)
  • 非抛出 函数是所有其他函数 (包括 noexcept 说明符的 expression 的值为 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

如果虚函数是非抛出的,则每个重写器的所有声明 (包括定义) 也必须是非抛出的,除非重写器定义为 deleted

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 规范 需要 但尚未实例化时,将查找依赖名称,并且 expression 中使用的任何模板都将实例化,就像为了特化的声明一样。

函数的 noexcept 规范在以下上下文中被认为是需要

  • 在表达式中,其中函数通过重载解析选择
  • 函数是 odr-used
  • 该函数本应是 odr-used 的,但出现在未求值操作数中
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 是对函数、函数指针或成员函数指针的函数调用,这些函数是可能抛出 (C++17 前),除非 e核心常量表达式(C++17 前)
  • e可能抛出 函数进行隐式调用 (例如重载运算符、new 表达式中的分配函数、函数参数的构造函数,或者如果 e 是完整表达式,则为析构函数)
  • ethrow-表达式
  • edynamic_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
};

[编辑] 备注

常量 expression 的用途之一是 (与 noexcept 运算符 一起) 定义为某些类型声明 noexcept 而不为其他类型声明 noexcept 的函数模板。

请注意,函数上的 noexcept 规范不是编译时检查;它仅仅是程序员告知编译器函数是否应抛出异常的一种方法。编译器可以使用此信息来对非抛出函数启用某些优化,以及启用 noexcept 运算符,该运算符可以在编译时检查特定表达式是否声明为抛出任何异常。例如,如果元素的移动构造函数是 noexcept,则诸如 std::vector 之类的容器将移动其元素,否则将复制 (除非复制构造函数不可访问,但是可能抛出移动构造函数是可访问的,在这种情况下,将放弃强异常保证)。

[编辑] 弃用

noexceptthrow() 的改进版本,throw() 在 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++ 标准。

DR 应用于 已发布行为 正确行为
CWG 1330 C++11 异常规范可能被急切实例化 它仅在需要时才实例化
CWG 1740 C++11 noexcept 后的 ( 可能启动初始化器 它只能是...的一部分
noexcept 规范
CWG 2039 C++11 只需要转换前的表达式是常量 转换也必须是
在常量表达式中有效

[编辑] 参见

noexcept 运算符(C++11) 确定表达式是否抛出任何异常[编辑]
动态异常规范(C++17 前) 指定函数抛出哪些异常 (C++11 中已弃用) [编辑]
throw 表达式 发出错误信号并将控制权转移给错误处理程序[编辑]
如果移动构造函数不抛出异常,则将参数转换为 xvalue
(函数模板) [编辑]