noexcept修饰函数类型,须置于声明末尾;标为true时若抛异常则调用std::terminate(),false表示可能抛(默认);移动操作和析构函数建议添加以支持容器优化,但需确保无异常出口。

noexcept 用在函数声明里到底改写哪儿
它只修饰函数类型,不是函数体里的语句,也不能当运行时判断用。写错位置会导致编译失败或语义完全偏离预期。
- 正确位置是函数声明末尾,紧挨着参数列表右括号之后,例如:
void foo() noexcept;或int bar(int x) noexcept(true); - 不能写成
noexcept { ... },那是 C++20 的constexpr函数体语法,和noexcept无关 - 成员函数要连同
const/&/&&一起写,比如:std::string& operator=(std::string&&) noexcept; - 模板函数中若依赖模板参数是否抛异常,得用
noexcept(expression)形式,比如:noexcept(std::is_nothrow_move_constructible_v<t>)</t>
noexcept(true) 和 noexcept(false) 有啥实际区别
区别不在“能不能抛”,而在“编译器信不信你”。一旦标了 noexcept,函数内若真抛了异常,程序直接调用 std::terminate() —— 不会栈展开,不触发 catch,秒崩。
-
noexcept等价于noexcept(true),表示承诺不抛;noexcept(false)是显式声明可能抛,但几乎没人这么写(默认就是 false) - 移动构造/移动赋值被标记为
noexcept,std::vector才敢在扩容时用移动而非拷贝(否则降级为拷贝,性能掉一截) - 如果误标
noexcept却调用了可能抛的函数(比如没检查new失败、没加try/catch包裹外部 API),运行时崩溃无法捕获
哪些函数必须/建议加 noexcept
不是所有函数都适合加,加错反而锁死接口演进能力。重点看标准库容器是否依赖它做优化,以及是否真能保证不抛。
- 移动操作:只要内部只做指针交换、
std::swap、std::move等无异常操作,就该加 ——std::vector、std::deque扩容时会查这个 - 析构函数:C++11 起默认是
noexcept(true),显式写出来更清晰;若析构里调了可能抛的代码(比如关闭文件时fclose返回 EOF 并 log),就得改成noexcept(false)或处理掉异常源 - 普通业务函数别乱加:比如网络请求封装、JSON 解析,底层调了
throw或std::string::at(),硬标noexcept就是埋雷
怎么验证 noexcept 是否生效
不能光看编译过没过,得确认编译器真把它当类型属性用了。最直接的方式是查 std::is_nothrow_xxx 类型特征,或者观察容器行为变化。
立即学习“C++免费学习笔记(深入)”;
- 用
static_assert检查:static_assert(std::is_nothrow_move_constructible_v<mytype>);</mytype> - 给
std::vector<mytype></mytype>加一堆元素再resize,用调试器看是否调用了移动构造(而非拷贝);若没加noexcept,即使你写了移动函数,vector也可能不用 - Clang/GCC 编译时加
-fsanitize=undefined,运行时若违反noexcept承诺,会报runtime error: call to a function declared with "throw()" attribute
真正难的不是语法怎么写,而是判断“我这段逻辑到底有没有隐藏的异常出口”——比如第三方库回调、系统调用 errno 检查遗漏、std::optional::value() 未检查是否有值。这些地方标了 noexcept 就等于主动放弃错误传播能力。










