15 August 2023

C++ 核心指南目录

“Don’t use exception specification”

理由

异常规格说明导致错误处理变的很脆弱,引起额外的运行时开销。所以这个功能已经从标准 C++ 中移除了。

例子

int use(int arg)
    throw(X, Y)
{
    // ...
    auto x = f(arg);
    // ...
}

如果 f() 抛出的异常不属于 XY 就会调用到未定义的出错处理过程,默认的行为是程序终止。这样看起来没问题,但是如果我们修改了 f() ,让它抛出 Z 异常,这时候,我们的 use 会因为 Z 异常而终止,除非我们修改 use() 的异常规格说明,这时候可能还需要重新测试所有代码。困难的地方是,这个 f() 函数可能是我们无法控制的程序库函数,它抛出的新的异常 use() 可能不关心、也不知道怎么处理。这时候,我们可以修改 use() 函数,让它把新的异常 Z 转交给调用 use() 的函数来处理。但是这时候,调用 use() 的函数可能需要修改了。很快,这个事情就会变的很不可控。我们也可以在 use()try-catch 捕捉 Z 异常。但是这个方法也很快变的难以管理。请注意,修改总的异常种类可能涉及到系统的最底层。比如修改某些网络库或者中间件。所以,这些修改会像肥皂泡一样,一直上升到调用链的最上面。在大的代码库中,这就意味着,只有最后一个用户修改了代码之后,整个代码库才可以切换到最新版本的库。如果 use() 属于程序库,可能会因为某些未知的客户在使用,我们就无法升级它。

“让异常一直传递到某个能处理的函数”,这个策略多年来一直证明是有效的方法。

注意

用异常规格说明的方式静态地进行强化实际上并无益处。

注意

如果没有异常要抛出,请使用 noexcept

强化

标记出现异常规格说明的地方。