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

noexcept 是 C++11 引入的关键字,用来显式声明一个函数**不会抛出任何异常**。它既是编译器的承诺,也是调用者的契约——一旦标记为 noexcept,函数体内若意外抛出异常(且未被捕获),程序将立即调用 std::terminate() 终止执行,而不是尝试栈展开。
noexcept 的核心作用:优化与安全边界
它不只是“说明不抛异常”,更直接影响编译器行为和标准库决策:
-
启用移动操作的强保证:例如
std::vector::resize()在重新分配内存时,若元素类型的移动构造函数是noexcept,就会优先选择移动而非复制,显著提升性能;否则可能退化为复制以维持异常安全。 -
影响函数类型签名:
void f() noexcept和void 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::swap、std::unique_ptr 的移动操作都要求 noexcept,否则容器在异常场景下无法保证强安全。
基本上就这些。noexcept 不复杂但容易忽略,关键是把它当作接口契约来设计,而不是事后补的性能标签。









