0

标题说明了一切:为什么 C++ 放弃了完全令人满意、有用的空抛出规范throw(),并用另一种语法替换它,并引入了 new 关键字noexcept

空抛出规范是“仅抛出这些枚举异常的保证”(书面),但枚举异常为零:您可以抛出空集,throw(X,Y,Z)而不是抛出X, Yor (和派生类型):这是对函数的保证从不向调用者扔任何东西,换句话说,从不扔,或“不扔”规范Z

那无偿使用基本相同的工具制作新代码,表达相同的承诺,与旧代码不兼容,旧代码被弃用然后被禁止,无缘无故地破坏向后兼容性?

是什么引起了这样的仇恨throw()

据我所知,只有旧的不安全gets和愚蠢无用的隐含int被严厉对待。

编辑:

所谓的“重复”是基于虚假陈述。

在所谓的“动态异常规范”中没有“动态”。这是我对新的 throw 规范最讨厌的地方:反对“动态”和“静态”的空洞术语的含义。

4

1 回答 1

4

无偿使用基本相同的工具制作新代码,表达相同的承诺,与旧代码不兼容

更正:如果一个函数违反了动态异常规范,std::unexpected则调用它,它调用默认情况下调用的意外处理程序std::terminate。但是处理程序可以由用户函数替换。如果函数违反noexcept,std::terminate则直接调用。

承诺不一样。throw()意思是“如果它试图发出异常,可能会做意想不到的事情”。noexcept表示“如果它试图发出异常,将立即终止。”

只有在 C++17 中,它才throw()完全等同于noexcept. 这是在 6 年的异常规范(包括throw())被弃用之后。

应该注意的是,在第一篇论文unexpected中明确引用了这种差异。具体来说:noexcept

请注意,noexcept(true)作为优化提示的有用性远远超出了 N2855 引入的狭窄情况。事实上,它超越了移动构造:当编译器可以确定地检测到非抛出操作时,它可以优化掉大量用于异常处理的代码和/或数据。一些编译器已经为规范做到了这一点throw(),但由于这些会产生隐式 try/catch 块来处理意外异常的开销,因此好处是有限的。

隐式 try/catch 块是必要的,因为unexpected必须在将堆栈展开到throw()函数后调用。本质上,每个throw()函数看起来像这样:

void func(params) throw()
try
{
  <stuff>
}
catch(...)
{
  std::unexpected();
}

因此,当异常试图离开throw()函数时,异常被捕获并且堆栈展开。更重要的是,每个throw()函数都必须内置异常机制。因此,每个功能try/catch都会产生块产生的任何成本。throw()

在 的第一个版本中noexcept,发出异常的是直接 UB,而后来的版本切换为std::terminate. 但即便如此,也不能保证平仓。因此实现可以noexcept以更有效的方式实现。当系统在堆栈中查找最近的catch子句时,如果它到达函数的底部noexcept,它可以直接终止而无需任何捕获机制。

是什么导致了对 throw() 的厌恶?

更正:不重新利用构造并不意味着恶意。尤其是当发明一个新的结构可以避免兼容性破坏时,如上所示。

应当注意,就表达式而言,它被throw()认为等同于noexcept函数。noexcept也就是说,调用throw()函数不会抛出异常,noexcept(empty_throw())表达式会产生true.

还应该注意的是,无论如何都需要一个新的关键字。为什么?因为throw(<stuff>)在 C++98/03 中已经有了意义。noexcept(<stuff>)有非常不同的含义<stuff>。从解析的角度来看,试图将这些noexcept东西放在说明throw符中会......很困难。

另外,您现在可以将此新关键字用作通用表达式:noexcept(<expression>)解析为true如果其中没有任何调用将引发异常。这允许您根据事物是否会引发异常来执行条件逻辑。你需要一个新的关键字来做这样的事情(或者你必须制作丑陋的语法,而 C++ 有太多的事情要做)。

于 2019-10-27T02:06:33.310 回答