无偿使用基本相同的工具制作新代码,表达相同的承诺,与旧代码不兼容
更正:如果一个函数违反了动态异常规范,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++ 有太多的事情要做)。