C++中的noexcept关键字有什么作用?C++异常安全规范详解【现代C++】

12次阅读

noexcept是c++11引入的关键字,用于声明函数不抛异常,违反承诺将调用std::terminate;它影响编译器优化、标准库行为(如移动操作选择)、函数类型签名,并支持编译期异常检查。

C++中的noexcept关键字有什么作用?C++异常安全规范详解【现代C++】

noexcept 是 C++11 引入的关键字,用来显式声明一个函数**不会抛出任何异常**。它既是编译器的承诺,也是调用者的契约——一旦标记为 noexcept,函数体内若意外抛出异常(且未被捕获),程序将立即调用 std::terminate() 终止执行,而不是尝试展开。

noexcept 的核心作用:优化与安全边界

它不只是“说明不抛异常”,更直接影响编译器行为和标准库决策:

  • 启用移动操作的强保证:例如 std::vector::resize() 在重新分配内存时,若元素类型的移动构造函数noexcept,就会优先选择移动而非复制,显著提升性能;否则可能退化为复制以维持异常安全。
  • 影响函数类型签名void f() noexceptvoid f() 是两种不同的函数类型,不能相互赋值或重载(除非仅 noexcept 性质不同,C++17 起允许这种重载)。
  • 支持 noexcept 运算符判断:可用 noexcept(expr) 在编译期检查某表达式是否不抛异常,常用于模板 SFINAE 或 constexpr if 分支。

noexcept 的两种写法:简单声明 vs. 条件表达式

基本形式:void func() noexcept; 表示硬性承诺不抛异常。

更灵活的是带条件表达式的写法:void func() noexcept(noexcept(other_func()));

立即学习C++免费学习笔记(深入)”;

  • 右边的 noexcept(...) 是运算符,返回 bool 编译期常量
  • 整个声明表示:“本函数是否 noexcept,取决于 other_func() 是否 noexcept”。这是实现“异常中立”(exception-neutral)接口的关键技巧。
  • 常见于模板函数,如自定义容器的移动构造:MyContainer(MyContainer&& rhs) noexcept(noexcept(std::declval().move()))

noexcept 不是万能的:常见误区

它不提供运行时保护,也不自动让函数变安全:

  • noexcept 函数仍可调用会抛异常的函数——只要你在内部捕获了它们;否则直接终止程序。
  • 析构函数默认是 noexcept(true)(C++11 起),所以务必确保析构中不抛异常,或显式写成 ~T() noexcept(false)(极少需要)。
  • 不要为了“看起来高效”盲目加 noexcept。违反承诺的代价是静默崩溃(std::terminate),比异常更难调试。

异常安全的三个等级,noexcept 对应最强一级

C++ 社区通常把异常安全分为三类:

  • 基本保证:失败后对象仍处于有效但未指定状态(如部分插入后容器仍可用)。
  • 强烈保证:失败后对象状态完全回滚,如同操作从未发生(常见于 copy-and-swap)。
  • 不抛保证(noexcept):根本不会失败,也不抛异常——这是最高级的异常安全,也是唯一能被编译器和标准库信任并据此优化的级别。

比如 std::swapstd::unique_ptr 的移动操作都要求 noexcept,否则容器在异常场景下无法保证强安全。

基本上就这些。noexcept 不复杂但容易忽略,关键是把它当作接口契约来设计,而不是事后补的性能标签。

text=ZqhQzanResources