nothrow保证即noexcept保证,指函数绝不会抛出任何异常,违反时程序调用std::terminate()终止;C++11起统一用noexcept说明符,其对move操作和容器优化至关重要。

什么是 nothrow 保证(noexcept guarantee)
在 C++ 中,nothrow 保证(更准确应称 noexcept guarantee)是异常安全的最高级别:函数承诺**绝不会抛出任何异常**。它不是“可能不抛”,而是编译器可验证、运行时强制的契约——若违反(比如 noexcept 函数内部抛了异常),程序会立即调用 std::terminate() 终止。
注意:nothrow 是旧式写法(C++98/03 中用于 new 表达式),C++11 起统一用 noexcept 说明符或说明符表达式。现在说 “nothrow 保证” 实际指 noexcept 语义下的强保证。
如何声明和验证 noexcept 函数
声明方式直接、显式,但容易误用:
-
void f() noexcept;—— 明确承诺不抛异常 -
void g() noexcept(true);—— 等价于上一行 -
void h() noexcept(false);—— 明确允许抛异常(默认行为) -
template—— 使用void swap(T& a, T& b) noexcept(noexcept(a.swap(b))); noexcept操作符做条件推导
关键点:
立即学习“C++免费学习笔记(深入)”;
- 编译器不会自动推导
noexcept,即使函数体为空或只调用其他noexcept函数,也必须显式标注 -
noexcept是函数类型的一部分:void(*)() noexcept和void(*)()是不同类型,不能互相赋值 - 析构函数默认是
noexcept的;若手动声明为noexcept(false),且实际抛异常,仍会触发std::terminate()
为什么 noexcept 保证对 move 操作和容器至关重要
标准库容器(如 std::vector)在扩容、重排时是否启用移动而非拷贝,取决于元素类型的移动操作是否具备 noexcept 保证:
-
std::vector::push_back在需要重新分配时,若T的移动构造函数是noexcept,则优先移动;否则退回到拷贝(因为拷贝失败可回滚,移动若抛异常则无法保证强异常安全) -
std::vector::resize、std::sort等算法也会依赖noexcept移动来启用优化路径 - 自定义类型若提供移动操作,务必评估其是否真能不抛异常;若不能,就别标
noexcept,否则破坏容器行为
struct Widget {
Widget(Widget&&) noexcept; // ✅ 若底层资源转移无异常风险(如 raw pointer 交换)
// Widget(Widget&&) { /* 可能抛 std::bad_alloc */ } // ❌ 不该标 noexcept
};常见陷阱与误判场景
看似安全的操作,实际可能隐式抛异常:
-
delete p;本身不抛,但若p是unique_ptr且其自定义删除器抛异常,则整个移动/销毁可能破环noexcept -
std::vector::data()是noexcept,但std::vector::at()不是(下标越界抛std::out_of_range) - 调用第三方库函数前,必须查文档或源码确认其是否真正
noexcept;仅看函数签名不够(比如未标注的函数默认noexcept(false)) - 模板函数中使用
noexcept表达式时,若未覆盖所有实例化分支,可能在某些类型下意外变成noexcept(false)
最易被忽略的是:异常安全级别不是静态属性,它依赖整个调用链的每一步——哪怕一个 noexcept 函数里调用了未标记的普通函数,整条路径就失去 noexcept 保证。











