noexcept 是编译期无开销的异常承诺,throw() 是运行期有开销且已废弃的动态规范;前者直接 terminate,后者调用 unexpected;noexcept 支持常量表达式和编译时探测,影响重载、优化及标准库实现。

noexcept 和 throw() 的核心区别在于:前者是编译期、无开销、语义明确的异常承诺;后者是运行期、有开销、已被淘汰的动态异常规范。
语法与语义本质不同
throw() 是 C++98 引入的动态异常规范,写在函数声明末尾,表示“该函数不抛出任何异常”。但它本质是运行时检查机制——若违反,先调用 std::unexpected(),再可能终止程序。
noexcept 是 C++11 引入的关键字,本身是编译期常量表达式。noexcept 或 noexcept(true) 表示“绝不抛出”,一旦抛出,直接调用 std::terminate(),跳过所有异常栈展开逻辑。
- throw() 是历史遗留写法,C++17 起已正式废弃,C++20 中完全移除(仅保留 throw() 作为 noexcept(true) 的兼容别名)
- noexcept 可带参数:noexcept(expr) 中的 expr 必须是编译期可判定的常量表达式,例如 noexcept(std::is_nothrow_move_constructible_v
) - noexcept 运算符(如 noexcept(func()))用于编译时探测表达式是否可能抛出,返回布尔值,不执行 func()
性能与优化能力差异显著
throw() 声明无法被编译器可靠信任——它不参与重载决议,也不影响内联或调用约定选择,因为实际行为只能在运行时验证。
立即学习“C++免费学习笔记(深入)”;
noexcept 提供强契约保证,编译器可据此激进优化:
- 移动操作(如 vector 扩容)会优先选用 noexcept 的移动构造/赋值函数;否则退回到更慢的拷贝路径
- 某些 ABI(如 Itanium)对 noexcept 函数使用更紧凑的调用约定,省去异常处理元数据
- 模板元编程中可基于 noexcept 特性做 SFINAE 或 if constexpr 分支
标准库与现代实践全面转向 noexcept
C++11 后,标准库中几乎所有原本用 throw() 声明的函数(如 operator new、析构函数、swap 等)都已改用 noexcept。例如:
- C++98:
void* operator new(std::size_t) throw(std::bad_alloc); - C++11+:
void* operator new(std::size_t) noexcept(false);(显式允许抛出) - 移动构造函数默认是 noexcept 的,除非成员中存在非 noexcept 的移动操作
用户自定义类型中,若希望 move 操作被容器高效利用,必须显式标注 noexcept,否则 std::vector 在 resize 时将避免移动而选择拷贝。
错误处理行为不可逆地简化
throw() 允许用户通过 set_unexpected 自定义 std::unexpected 处理器,带来调试灵活性但也引入不确定性;noexcept 彻底放弃这一层,抛出即 terminate——这是有意为之的设计取舍:用确定性换简洁性和性能。
这意味着 noexcept 更适合系统级、实时或资源敏感场景,而 throw() 的“可干预”特性反而成了维护负担和安全盲区。










