noexcept是编译器的异常承诺书,声明函数绝不抛异常;它禁用栈展开、启用移动优化,违者调用std::terminate,但不提供异常安全保证,也不校验函数体是否真无throw。

noexcept 是编译器的“异常承诺书”
它不是用来捕获或处理异常的,而是告诉编译器:这个函数**绝不会抛出任何异常**。一旦你加了 noexcept,编译器就信了——它会据此做两件事:一是跳过为该函数生成异常栈展开(stack unwinding)代码;二是允许某些优化(比如移动操作的强制选择)。如果你在 noexcept 函数里手动 throw 了异常,程序会直接调用 std::terminate(),而不是尝试 catch。
noexcept 有两种写法,行为完全不同
noexcept(无括号)表示**无条件不抛异常**;noexcept(expr) 是一个常量表达式,运行期前就能确定是否 noexcept——比如 noexcept(noexcept(other_func())) 这种双重检查写法很常见。
-
void f() noexcept;→ 编译器认为它铁定不抛,哪怕里面写了throw也会在运行时报std::terminate -
void g() noexcept(sizeof(int) == 4);→ 表达式为 true 才是 noexcept,否则等价于没加 - 移动构造函数和移动赋值运算符如果想被标准容器(如
std::vector)优先选用,必须是noexcept,否则容器可能退回到拷贝而非移动
加 noexcept 不等于性能一定提升
它主要影响的是异常处理机制的底层开销,以及标准库对移动语义的决策逻辑。在没有异常路径的函数里硬加 noexcept,编译器未必能生成更快的机器码;但若函数本就该无异常(比如析构函数、swap、简单访问器),不加反而可能让 std::vector::resize 或 std::sort 拒绝使用移动而降级为拷贝。
- 析构函数默认是
noexcept的,显式写成~T() noexcept更清晰,也避免被模板推导误判 - 不要给可能调用用户代码的函数随便加
noexcept,比如回调包装器,除非你 100% 控制所有路径 - Clang 和 GCC 在 -O2 下会对
noexcept函数省略部分栈保护指令,但 MSVC 需要 /EHsc 且对 noexcept 更敏感
常见误用:把 noexcept 当作异常安全保证
noexcept 不提供任何异常安全级别(basic/strong/no-throw guarantee),也不检查函数体是否真没 throw。它只是一个契约声明,由程序员负责守约。编译器只做静态校验:比如 noexcept 函数内调用了非 noexcept 函数,不会报错,但一旦那个被调函数抛了,就 terminate。
立即学习“C++免费学习笔记(深入)”;
- 别指望
noexcept能帮你发现潜在异常点——它不参与编译期检查 throw 行为 - 用
noexcept替代 try/catch 是危险的,二者解决的问题根本不同 - 模板中大量使用
noexcept表达式时,注意 SFINAE 和 C++20requires的交互,有些旧编译器对嵌套noexcept解析不稳定
new(可能抛 std::bad_alloc),就不能无条件加 noexcept。









