无偿使用基本相同的工具编写新代码,表达相同的承诺,与旧代码不兼容
更正:如果函数违反动态异常规范,则调用 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(<stuff>) 已经有了意义。 noexcept(<stuff>) 与 <stuff> 的含义截然不同。从解析的角度来看,尝试将 noexcept 内容放在 throw 说明符中会很困难。
另外,您现在可以使用这个新关键字作为通用表达式:noexcept(<expression>) 解析为 true,如果其中没有任何调用会引发异常。这允许您根据事物是否会引发异常来执行条件逻辑。你需要一个新的关键字来做这样的事情(或者你必须做出丑陋的语法,而 C++ 有太多的事情要做)。