异常规范 (throw、noexcept) (C++)

异常规范是一项 C++ 语言功能,指示程序员对可由函数传播的异常类型的意图。 可以使用异常规范指定函数可以或不可以因异常退出。 编译器可以使用此信息来优化对函数的调用,并在意外异常脱离函数时终止程序。

在 C++17 之前,有两种异常规范。 noexcept 规范是 C++11 中的新增规范。 它指定可以脱离函数的潜在异常集是否为空。 动态异常规范throw(optional_type_list) 规范)在 C++11 中已弃用,并已在 C++17 中删除,但 throw() 除外,它是 noexcept(true) 的别名。 此异常规范原本用来提供有关可从函数引发哪些异常的摘要信息,但在实际应用中发现此规范存在问题。 证明确实有一定用处的一个动态异常规范是无条件 throw() 规范。 例如,函数声明:

void MyFunction(int i) throw();

告诉编译器函数不引发任何异常。 但是,在 /std:c++14 模式下,如果函数确实引发异常,这可能会导致未定义的行为。 因此,建议使用 noexcept 运算符而不是上述运算符:

void MyFunction(int i) noexcept;

下表总结了 Microsoft C++ 的异常规范实现:

异常规范 含义
noexcept
noexcept(true)
throw()
函数不会引发异常。 在 /std:c++14 模式(默认)下,noexceptnoexcept(true) 是等效的。 从声明为 noexceptnoexcept(true) 的函数引发异常时,将调用 std::terminate。 当在 /std:c++14 模式下从声明为 throw() 的函数引发异常时,结果为未定义的行为。 未调用任何特定函数。 这与 C++14 标准不同,后者要求编译器调用 std::unexpected
Visual Studio 2017 版本 15.5 及更高版本:在 /std:c++17 模式下,noexceptnoexcept(true)throw() 都是等效的。 在 /std:c++17 模式下,throw()noexcept(true) 的别名。 在 /std:c++17 模式和更高版本模式中,当从用上述任何规范声明的函数引发异常时,将按 C++17 标准要求调用 std::terminate
noexcept(false)
throw(...)
无规范
函数可以引发任何类型的异常。
throw(type) (C++14 和更早版本)函数可以引发 type 类型的异常。 编译器接受语法,但将其解释为 noexcept(false)。 在 /std:c++17 模式和更高版本模式中,编译器发出警告 C5040。

如果在应用程序中使用异常处理,则调用堆栈中必须有一个函数,其在引发的异常退出标记 noexceptnoexcept(true)throw() 的函数的外部范围之前处理这些异常。 如果在引发异常的函数与处理异常的函数之间调用的任何函数都指定为 noexceptnoexcept(true)(或在 /std:c++17 模式下为 throw()),则当 noexcept 函数传播异常时,程序将终止。

函数的异常行为基于以下因素:

  • 设置了哪种语言标准编译模式

  • 您是否在 C 或 C++ 下编译函数。

  • 所使用的 /EH 编译器选项。

  • 是否显式指定异常规范。

不允许对 C 函数使用显式异常规范。 假定 C 函数在 /EHsc 下不引发异常,并且可能会在 /EHs/EHa/EHac 下引发结构化异常。

下表总结了 C++ 函数是否可能会在各种编译器异常处理选项下引发:

函数 /EHsc /EHs /EHa /EHac
没有异常规范的 C++ 函数
带有 noexceptnoexcept(true)throw() 异常规范的 C++ 函数 No
带有 noexcept(false)throw(...)throw(type) 异常规范的 C++ 函数

示例

// exception_specification.cpp
// compile with: /EHs
#include <stdio.h>

void handler() {
   printf_s("in handler\n");
}

void f1(void) throw(int) {
   printf_s("About to throw 1\n");
   if (1)
      throw 1;
}

void f5(void) throw() {
   try {
      f1();
   }
   catch(...) {
      handler();
    }
}

// invalid, doesn't handle the int exception thrown from f1()
// void f3(void) throw() {
//   f1();
// }

void __declspec(nothrow) f2(void) {
   try {
      f1();
   }
   catch(int) {
      handler();
    }
}

// only valid if compiled without /EHc
// /EHc means assume extern "C" functions don't throw exceptions
extern "C" void f4(void);
void f4(void) {
   f1();
}

int main() {
   f2();

   try {
      f4();
   }
   catch(...) {
      printf_s("Caught exception from f4\n");
   }
   f5();
}
About to throw 1
in handler
About to throw 1
Caught exception from f4
About to throw 1
in handler

另请参阅

trythrowcatch 语句 (C++)
现代 C++ 处理异常和错误的最佳做法