【问题标题】:Why was the old empty throw specification rewritten with a new syntax `noexcept`?为什么旧的空抛出规范被重写为新语法“noexcept”?
【发布时间】:2020-02-22 20:09:00
【问题描述】:

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

空抛出规范是“只抛出这些枚举异常的保证”(写成throw(X,Y,Z)),但枚举异常为零:而不是抛出X、Y 或Z(以及派生类型),你可以抛出空集:保证函数永远不会向调用者抛出任何东西,换句话说,就是永不抛出或“不抛出”规范。

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

是什么导致throw()如此讨厌?

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

编辑:

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

在所谓的“动态异常规范”中没有什么“动态”。这是我对新的抛出规范最讨厌的地方:反对“动态”和“静态”。

【问题讨论】:

标签: c++ exception language-design noexcept exception-specification


【解决方案1】:

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

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

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

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

应该注意unexpected 的区别在first papers about noexcept 中被明确引用。具体来说:

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

隐式 try/catch 块是必要的,因为必须在将堆栈展开到 throw() 函数后调用 unexpected。本质上,每个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。

还应注意,无论如何都需要一个新的关键字。为什么?因为 C++98/03 中的throw(&lt;stuff&gt;) 已经有了意义。 noexcept(&lt;stuff&gt;) 与 &lt;stuff&gt; 的含义截然不同。从解析的角度来看,尝试将 noexcept 内容放在 throw 说明符中会很困难。

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

【讨论】:

  • "调用了意外的处理程序" 是的,我完全忘记了那个,可能是因为我没有看到set_unexpected 的任何实际用途。因此,意外的处理程序主要是一个复杂性,对于空抛出规范没有任何改变。
  • 有趣的答案,但我在这里不同意:“这不是动态异常说明符可以做的事情。”他们实际上可以做到:throw(...)
  • @curiousguy:我没有 C++98/03 的副本,但我不记得 throw(...) 是合法的语法。 grammar seems to require a list of type-ids,我不认为 ... 是类型 ID。
  • 我确定 throw(...) 存在于 97 年投票的 C++ 标准中,除了(故意)它没用而且没有人使用它。
  • @curiousguy:我对这种语法的记忆有限,但我记得很模糊,所以我删除了这一段。我还添加了一些关于unexpected 的细节,以及关于原始noexcept 提案的一些细节。
猜你喜欢
  • 2016-09-22
  • 2020-11-09
  • 2012-01-04
  • 1970-01-01
  • 1970-01-01
  • 2013-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多