条款14:如果函数不抛出异常请使用 noexcept
条款14:如果函数不抛出异常请使用 noexcept
为什么使用 noexcept?
- 更安全的接口设计:
noexcept是函数接口的一部分,明确告知调用者「不会抛异常」。 - 启用更多优化:
编译器对noexcept函数可以省略异常处理机制,生成更高效代码。 - 支持 STL 的强异常安全保证:
如std::vector::push_back、std::swap等需要知道操作是否会抛异常,才能决定是否使用移动操作。
noexcept VS 旧式异常说明
| 写法 | 说明 |
|---|---|
void f() throw(); |
C++98 写法,已废弃 |
void f() noexcept; |
C++11+ 写法,推荐 |
void f(); |
无声明,不清楚是否抛异常 |
应该在哪些函数上加 noexcept?
- 明确不会抛异常的函数
- 移动构造 / 移动赋值(性能关键)
swap函数- 析构函数(默认隐式
noexcept,除非成员可能抛异常) - delete 操作符(默认
noexcept)
STL 是如何利用 noexcept 的?
-
STL 会使用
std::move_if_noexcept():if (is_nothrow_move_constructible<T>::value || !is_copy_constructible<T>::value) std::move(t); // 否则保守地使用复制 -
std::swap是否noexcept取决于你提供的类型T的swap是否noexcept:template<typename T> void swap(T& a, T& b) noexcept(noexcept(a.swap(b)));这是一个经典的 条件 noexcept:
- 如果
a.swap(b)本身是noexcept的,那swap也是; - 如果
a.swap(b)有抛异常的可能,那swap也不是noexcept。
- 如果
| 概念 | 含义 |
|---|---|
noexcept |
声明函数不抛异常 |
noexcept(expr) |
编译时判断某个表达式是否抛异常,返回布尔值 |
noexcept(noexcept(expr)) |
用在函数声明中,形成“条件 noexcept” |
不要滥用 noexcept
不要为“异常中立”函数加上 noexcept:
void logToFile(const std::string& msg); // 可能内部 I/O 抛异常
void doSomething() noexcept { // ❌ 错误声明
logToFile("doing work"); // 若抛异常将终止程序
}
宽泛契约 vs 严格契约
| 类型 | 特征 | 是否建议加 noexcept |
|---|---|---|
| 宽泛契约 | 没有前置条件,任意状态下都能调用 | 建议加 noexcept |
| 严格契约 | 有前置条件,违反时可能未定义行为 | 若使用异常报告前置条件冲突,则不要加 noexcept |
示例:移动函数加 noexcept
class Widget {
public:
Widget(Widget&&) noexcept; // 推荐加
Widget& operator=(Widget&&) noexcept; // 推荐加
};
C++ 默认 noexcept 的情况
- 析构函数和
operator delete默认隐式noexcept - 只有当其成员的析构函数为
noexcept(false)才不为noexcept
实用建议
- 给移动构造 / 移动赋值 /
swap明确加上noexcept - 如果写的函数真的不会抛异常,一定要加上
noexcept - 但不要为了加
noexcept而故意扭曲实现或隐藏错误(如用状态码替代异常)

浙公网安备 33010602011771号