noexcept是编译期契约,声明函数绝不抛异常;违反则直接调用std::terminate终止程序。最应添加的场景是移动构造/赋值、析构函数及确定无异常的工具函数。

noexcept 用来告诉编译器“这个函数绝不会抛出异常”
它不是运行时检查,而是编译期契约:一旦你标记了 noexcept,函数体内若出现未捕获的异常(比如调用了一个可能抛异常的函数又没处理),程序会直接调用 std::terminate() 终止,而不是栈展开。这能避免异常传播开销,也让编译器有机会做更激进的优化(比如移动操作、内联判断)。
哪些函数加 noexcept 最有价值?
主要是移动构造函数、移动赋值运算符、析构函数,以及你明确控制所有子调用且已确认无异常路径的工具函数。标准库容器在做内存重分配或元素移动时,会优先选择 noexcept 的移动操作——否则退回到更慢的拷贝。
- 移动构造函数不加
noexcept,std::vector::resize()可能拒绝移动而改用拷贝 - 析构函数默认是
noexcept(true);显式写成~T() noexcept更清晰 - 普通成员函数加
noexcept前,务必确认所有调用链(包括new、operator[]、自定义函数)都不抛异常
noexcept 后面可以跟括号表达式,别乱用
noexcept 不只是个关键字,它是个运算符,支持 noexcept(expr) 形式,返回 bool 编译期常量。常见写法:
void f() noexcept(noexcept(g()) && noexcept(h())); // 只有 g 和 h 都不抛,f 才 noexcept
但注意:noexcept(g()) 检查的是 g 的声明,不是运行时行为;如果 g 声明为 noexcept,那整个表达式就是 true。滥用嵌套会让代码难读,多数场景直接写 noexcept 就够了。
立即学习“C++免费学习笔记(深入)”;
加了 noexcept 却意外抛异常,后果很直接
不是报错或警告,而是运行时崩溃——调用 std::terminate(),没有栈展开,资源不自动释放。调试时容易误判为段错误。常见踩坑点:
- 在
noexcept函数里调用了没加noexcept的第三方函数(比如某些 STL 算法未标注) - 写了
throw;或throw std::runtime_error(...);—— 这是硬编码违规 - 使用
new时没配std::nothrow,而系统内存不足触发std::bad_alloc - 访问
std::vector::operator[]越界不抛异常,但at()会抛 —— 别在noexcept函数里用at()
真正关键的不是“能不能加”,而是“敢不敢保证”。一旦加了,就得对整条调用链负全责。









