noexcept是C++11引入的异常说明符,必须置于函数声明右括号后、const/volatile前,正确形式为void f() noexcept;或void f() noexcept(expr);,错误写法如void f(noexcept)或void f() noexcept()。

noexcept 用在函数声明末尾,不是括号里
很多人第一反应是写成 void func(noexcept) 或 void func() noexcept(),这是错的。noexcept 是说明符,必须紧贴在函数声明的右括号之后、const/volatile/ref-qualifier 之前,且不带括号(除非你要传表达式)。
正确写法只有两种基本形式:
-
void func() noexcept;—— 表示该函数承诺绝不抛异常(编译期检查,违反则调用std::terminate) -
void func() noexcept(true);和void func() noexcept(false);—— 显式传布尔常量,语义同上
noexcept 后面跟表达式时,会在实例化时求值
比如 template<typename t> void swap(T& a, T& b) noexcept(noexcept(a.swap(b)));</typename>,外层 noexcept 是说明符,内层 noexcept(...) 是运算符(注意括号),用来检测 a.swap(b) 是否被声明为 noexcept。这个判断发生在模板实例化时,不是编译一开始就定死的。
常见误用是写成 noexcept(sizeof(T) > 4) 这类无意义表达式——它语法合法,但和异常无关,纯属误导自己。
立即学习“C++免费学习笔记(深入)”;
实用场景集中在:
- 移动构造/移动赋值:标准容器要求
noexcept移动操作才能触发“移动而非拷贝”优化 - 析构函数:默认是
noexcept,显式写~T() noexcept更清晰;若析构里可能抛异常,必须写noexcept(false),否则隐式调用std::terminate
noexcept 不等于“没 try/catch”,也不阻止 throw
加了 noexcept 的函数里仍可写 throw,只是运行时一旦抛出,会立即调用 std::terminate,不会栈展开。它不是编译器拦住你 throw,而是“违约惩罚”。
所以要注意:
- 别在
noexcept函数里调用可能抛异常的第三方函数,除非你 100% 确认其声明也是noexcept -
std::vector::push_back在需要扩容时可能调用元素的拷贝/移动构造——如果这些操作不是noexcept,push_back自身就不能是noexcept,这会影响你的容器行为 - 虚函数的
noexcept声明必须与基类一致,否则编译报错:noexcept是函数类型的一部分
怎么查一个函数是不是 noexcept?
不能靠肉眼猜,尤其模板或重载函数。用 noexcept 运算符配合 decltype 测试:
static_assert(noexcept(func(42)), "func(int) must be noexcept"); static_assert(!noexcept(std::vector<int>().at(0)), "at() throws on bad index");
注意:这个运算符只看声明,不看函数体;它返回 bool 编译期常量,适合 static_assert 或 enable_if。
真正容易被忽略的是:析构函数、移动操作、swap 这三类函数的 noexcept 状态,直接决定标准库容器能否安全执行移动、重新分配、异常安全回滚等关键行为。写错一个,可能让 vector 在扩容时默默降级成拷贝,或者异常发生时直接终止程序。


















