09 April 2022

C++ 核心指南目录

F.6: If your function must not throw, declare it noexcept

理由

如果代码中不打算抛出异常,程序里也不打算处理异常,可以直接终止这个程序。声明一个函数为 noexcept 可以让编译器优化生成的二进制、并且可以减少执行路径,加快错误时的退出速度。

例子

把不支持异常的 C 或其他语言实现的函数标注为 noexcept 。C++ 标准库默认所有 C 标准库函数为 noexcept

注意

constexpr 函数在运行时计算可能会抛出异常,所以某些情况可以 noexcept 。根据条件判断是否抛出异常。

例子

我们甚至可以在可能抛出异常的函数上添加 noexcept 标签。

vector<string> collect(istream& is) noexcept
{
    vector<string> res;
    for (string s; is >> s;)
        res.push_back(s);
    return res;
}

collect 运行时如果内存不足,程序直接退出。除非程序设计好能处理内存耗尽的情况。终止函数terminate()可能会产生一个错误日志信息。

例子

我们首先定义一个函数,抛出一个异常:

void throw_err() {
    throw 1;
}

然后,定义个没有标记 noexcept 的函数调用 throw_err :

void throw_err() {
    throw 1;
}
void no_block_throw() {
    throw_err();
}
int main()
{
    try {
        no_block_throw();
    } catch(int i) {
        cout << i << endl;
    }
    return 0;
}

异常成功捕捉:

1

在定义一个有 noexcept 的函数:

void throw_err() {
    throw 1;
}
void block_throw() noexcept {
    throw_err();
}
int main()
{
    try {
        block_throw();
    } catch(int i) {
        cout << i << endl;
    }
    return 0;
}

程序直接终止:

terminate called after throwing an instance of 'int'

注意

在决定是否用 noexcept 前,要评估程序的执行环境,是否有异常抛出和内存分配的问题。某些执行环境能处理分配异常bad_alloc等,标准库可以抛出异常。但是很多执行环境,无法正常处理异常,最直接最简单的办法就是终止程序。如果代码无法处理内存分配错误,最好的办法就是给可能导致内存分配错误的函数添加 noexcept

换句话说,如果能处理好异常的情况的话,就不要加 noexcept

noexcept 主要用于底层函数。

注意

析构函数、数据交换函数、转移操作还有默认析构函数不可抛出异常。

加强

  • 标注不能抛出异常,但是没有声明 noexcept 的函数
  • 标注不该抛出异常却抛出异常的交换、转移、析构函数、默认析构函数