noexcept关键字用于声明函数是否可能抛出异常。1. 基本用法是加在函数声明末尾表示不抛异常,帮助编译器检查错误并优化性能;2. 可带布尔表达式实现条件性异常说明,适用于泛型编程;3. 替代旧的throw()语法,具有更高效和统一的优势;4. 使用建议包括在移动构造、swap、析构函数中加noexcept,而在会抛异常或调用库函数时避免使用。

在C++中,
noexcept关键字的作用是用来说明一个函数是否可能抛出异常。简单来说,它是一个异常说明符,告诉编译器和开发者:“这个函数不会抛出异常”或者“可能会抛出异常”。这不仅影响程序的行为,还可能影响性能优化和接口设计。

1. noexcept
的基本用法:声明函数不抛异常
你可以把
noexcept加在函数声明或定义的末尾,表示该函数不会抛出任何异常:

void myFunction() noexcept;
这样做的好处是,如果在这个函数里你意外用了会抛异常的代码(比如
throw或调用了可能抛异常的函数),编译器会给出警告甚至报错,帮助你在编译阶段发现问题。
立即学习“C++免费学习笔记(深入)”;
- 如果你确定某个函数不会抛异常,加上
noexcept
可以提高效率,特别是在移动构造、swap等操作中。 - 如果你不加,编译器默认认为函数可能抛异常。
注意:如果你在noexcept函数里真的抛了异常,程序会直接调用std::terminate()终止执行,而不是进入catch块。
2. 带条件的noexcept
:根据类型特征决定是否抛异常
noexcept也可以带一个布尔表达式,表示只有当这个表达式为真时,函数才不会抛异常:
templatevoid maybeNoexceptFunc(T t) noexcept(noexcept(t.foo())) { t.foo(); }
上面的例子中,
maybeNoexceptFunc是否标记为
noexcept,取决于
t.foo()是否会抛异常。这种写法常用于泛型编程中,让模板函数的行为更灵活、安全。
常见的使用场景包括:
-
标准库容器的操作(如
vector
的移动构造函数) - 模板元编程中判断某些操作的安全性
- 自定义类型的交换函数(swap)
3. 和异常说明符throw()
的区别:历史版本和现代C++的差异
在早期C++标准中,我们用
throw()来表示函数不会抛异常:
void oldStyleFunc() throw();
但这种方式已经被C++11弃用,取而代之的是
noexcept。相比起来,
noexcept有几个优势:
- 更简洁直观,语法统一
- 不需要运行时检查异常表,效率更高
- 可以作为类型系统的一部分(比如函数指针类型)
所以现在写新代码时,应该优先使用
noexcept而不是旧的
throw()。
4. 使用建议:什么时候该加?什么时候不该加?
并不是所有函数都适合加
noexcept,以下是一些实用建议:
✅ 应该加的情况:
- 移动构造/赋值函数(如果不加,会影响容器性能)
- swap函数(很多标准库算法依赖于高效的非抛异常swap)
- 析构函数(默认析构函数是
noexcept
的,手动实现也应该保持一致)
❌ 不应该加的情况:
- 明确会抛异常的函数(比如错误处理、资源加载失败等情况)
- 函数内部调用了可能抛异常的库函数,并且你不打算捕获处理
举个例子:
class MyClass {
public:
MyClass(MyClass&& other) noexcept // 推荐加
: data(other.data) {
other.data = nullptr;
}
void loadData() { // 不推荐加
if (/* 文件读取出错 */) throw std::runtime_error("Load failed");
}
};基本上就这些。合理使用
noexcept不仅能提升性能,还能增强代码的健壮性和可维护性。不过别滥用,得根据实际情况来判断。










