noexcept 告诉编译器函数绝不会抛异常,从而跳过栈展开代码、启用更多优化(如移动替代拷贝);未标注时容器可能降级为拷贝语义;它是函数类型一部分,影响重载、模板推导与虚函数协变;noexcept(expr) 在编译期求值,但标错将导致 std::terminate。

noexcept 告诉编译器“这个函数绝不会抛异常”
编译器看到 noexcept,就敢做两件事:一是跳过为该函数生成异常栈展开(stack unwinding)的辅助代码;二是允许在更多场景下做内联或调用优化——因为不用预留异常传播路径了。比如 std::vector::push_back 在移动元素时若移动构造函数标了 noexcept,编译器才敢用移动而非拷贝,否则宁可多一次深拷贝也不敢冒异常中途崩溃的风险。
不加 noexcept 可能导致 move 操作被悄悄降级为 copy
这是最常踩的坑:你写了移动构造函数,但没加 noexcept,标准容器(如 std::vector、std::deque)在扩容重排时会退回到拷贝语义。原因很实际——异常安全要求:如果移动中途抛了异常,而原对象已被“掏空”,就没法保证强异常安全。
-
std::is_nothrow_move_constructible_v为false→ 容器默认不用你的移动构造 - 即使你手动写
std::move(x),只要目标类型未声明noexcept,某些算法(如std::sort的内部交换)也可能避开移动 - Clang/GCC 在
-O2下会检查noexcept并据此选择 memcpy 或逐成员移动
noexcept(true) 和 noexcept(false) 的行为差异很小,但声明方式影响大
显式写 noexcept(true) 和只写 noexcept 等价;noexcept(false) 则明确禁止编译器做任何异常相关优化。但注意:noexcept 是函数类型的一部分,会影响函数指针、模板推导、重载解析。
- 两个仅差
noexcept的函数可以重载:void f() noexcept;和void f();是不同签名 - 模板中用
decltype推导函数类型时,noexcept会被保留,可能意外导致 SFINAE 失败 - 虚函数加
noexcept后,所有重写函数也必须带(且不能更宽松),否则编译报错:error: looser throw specifier
noexcept 表达式里调用可能抛异常的函数,编译器会静默转成 noexcept(false)
写 noexcept(expr) 时,如果 expr 中调用了未标记 noexcept 的函数(哪怕它实际从不抛),整个表达式结果就是 false。这不是运行时检查,是编译期求值——所以别指望靠 noexcept(f()) 来“探测”函数是否真的抛异常。
立即学习“C++免费学习笔记(深入)”;
-
noexcept(std::sqrt(1.0))→true(std::sqrt是noexcept的) -
noexcept(my_func())→false,除非my_func显式声明为noexcept - 连
noexcept(throw)都是合法表达式,结果为false;但noexcept(noexcept(f()))才是真的“探测”语法
真正难的是权衡:标了 noexcept 却在运行时抛了异常,程序直接调 std::terminate,连栈都来不及收。所以不是“能加就加”,而是“确认它真不会抛、且调用链上所有依赖也都不会”。









