委员会非常清楚地考虑了(试图)抛出异常规范不允许的异常的代码被认为格式错误的可能性,并拒绝了这个想法。根据 15.4 美元/11 美元:
实现不应仅仅因为表达式在执行时抛出或可能抛出包含函数不允许的异常而拒绝表达式。[ 例子:
extern void f() throw(X, Y);
void g() throw(X) {
f(); // OK
}
调用f
格式正确,即使在调用时可能会f
抛出不允许的异常。—结束示例]Y
g
不管是什么促成了这个决定,或者它可能是什么,很明显这不是事故或疏忽的结果。
至于为什么做出这个决定,至少有一些可以追溯到与 C++11 的其他新特性的交互,例如移动语义。
移动语义可以使异常安全(尤其是强保证)更难执行/提供。当你进行复制时,如果出现问题,很容易“回滚”事务——销毁你所做的任何副本,释放内存,原始数据保持不变。仅当/当复制成功时,您才销毁原件。
使用移动语义,这更难——如果你在移动东西的过程中遇到异常,你已经移动的任何东西都需要移回原来的位置以恢复原来的顺序——但是如果移动构造函数或移动赋值运算符可以抛出,你可能会在尝试将事物移回以尝试恢复原始对象的过程中得到另一个异常。
将此与 C++11 可以/确实为某些类型自动生成移动构造函数和移动赋值运算符的事实相结合(尽管有很长的限制列表)。这些不一定保证不会引发异常。如果您明确编写移动构造函数,您几乎总是希望确保它不会抛出,这通常甚至很容易做到(因为您通常“窃取”内容,您通常只是复制一些指针 -容易做到没有例外)。但是,即使对于像std:pair
. 一对可以移动的东西和需要复制的东西变得很难处理好。
这意味着,如果他们决定在编译时强制执行nothrow
(和/或throw()
),一些未知(但可能相当大)数量的代码将被完全破坏——多年来一直运行良好的代码突然不会甚至用新的编译器编译。
除此之外,尽管它们没有被弃用,但动态异常规范仍保留在语言中,因此无论如何它们最终都会在运行时强制执行至少一些异常规范。
所以,他们的选择是:
- 破坏大量现有代码
- 限制移动语义,以便它们适用于更少的代码
- 继续(如在 C++03 中)在运行时强制执行异常规范。
我怀疑有人喜欢这些选择中的任何一个,但第三个显然似乎是最后一个不好的选择。