Seb*_*edl 66
标准的策略noexcept
是仅标记不能或不能失败的函数,而不是那些仅指定不抛出异常的函数.换句话说,所有具有有限域的函数(传递错误的参数并且您得到未定义的行为)都不会noexcept
,即使它们未被指定抛出也是如此.
被标记的函数是swap
(必须不会失败,因为异常安全通常依赖于它)和numeric_limits::min
(不能失败,返回基本类型的常量).
原因是实现者可能希望提供其库的特殊调试版本,这些版本会引发各种未定义的行为情况,以便测试框架可以轻松检测到错误.例如,如果您使用带有索引的索引vector::operator[]
,或者调用front
或back
使用空向量.有些实现想要在那里抛出一个异常(它们被允许:因为它是未定义的行为,它们可以做任何事情),但是noexcept
对这些函数强制执行标准会使这变得不可能.
Xin*_*ang 16
作为@SebastianRedl回答的补充:你为什么需要noexcept
?
你可能已经知道,a vector
有它的容量.如果它已满push_back
,它将分配更大的内存,将所有现有元素复制(或从C++ 11移动)到新的主干,然后将新元素添加到后面.
但是,如果在分配内存或将元素复制到新主干时抛出异常怎么办?
如果在分配内存期间抛出异常,则向量处于其原始状态.只需重新抛出异常并让用户处理它就可以了.
如果在复制现有元素期间抛出异常,则通过调用析构函数将销毁所有复制的元素,释放已分配的中继,并抛出异常以由用户代码处理.(1)
在摧毁一切之后,矢量恢复到原始状态.现在可以安全地抛出异常让用户处理它,而不会泄漏任何资源.
来到C++ 11时代,我们有一个强大的武器叫做move
.它允许我们从未使用的对象中窃取资源.的std ::矢量将使用move
时,它需要增加(或减少)的能力,只要该move
操作是noexcept.
假设在移动期间抛出异常,前一个中继与之前move
的中断不同:资源被盗,使向量处于中断状态.用户无法处理异常,因为所有内容都处于非确定状态.
这就是为什么std::vector
依赖于move constructor
被noexcept.
这是客户端代码如何依赖于noexcept
接口规范的演示.如果以后noexcept
不满足要求,以前依赖它的任何代码都将被破坏.
noexcept
?简短回答:异常安全代码很难写.
答案noexcept
很长:对实现接口的开发人员设置严格的限制.如果noexcept
要从接口中删除,客户端代码可能会像上面给出的向量示例一样被破坏; 但是如果你想创建一个界面noexcept
,你可以随时自由地进行.
因此,仅在必要时将接口标记为noexcept
.
在2013年的Going Native中,Scott Meyers谈到了上述情况,即如果没有noexcept
,程序的完整性就会失败.
我还写了一篇关于它的博客:https://xinhuang.github.io/posts/2013-12-31-when-to-use-noexcept-and-when-to-not.html