您可能知道C++ 11有noexcept关键字.现在丑陋的部分是这样的:
请注意,函数的noexcept规范不是编译时检查; 它只是程序员通知编译器函数是否应抛出异常的方法.
http://en.cppreference.com/w/cpp/language/noexcept_spec
那么这是委员会部分的设计失败还是他们只是把它作为编译作者的练习:)从某种意义上来说,正常的编译器会强制执行它,坏的仍然可以兼容?
顺便说一句,如果你问为什么没有第三个选项(也就是说不能做),原因是我可以很容易地想到一个(慢)方法来检查函数是否可以抛出.如果你将输入限制为5和7(也就是我保证文件不会包含5和7旁边的任何内容),问题就会失败,并且只有当你给它33时它才会抛出,但这不是一个现实的问题恕我直言.
Jer*_*fin 21
委员会非常清楚地认为,(试图)抛出异常规范不允许的异常的代码被认为是不正确的,并且拒绝了这个想法.根据$ 15.4/11:
实现不应仅仅因为执行时抛出或抛出包含函数不允许的异常而拒绝表达式.[例如:
extern void f() throw(X, Y);
void g() throw(X) {
f(); // OK
}
Run Code Online (Sandbox Code Playgroud)
该呼叫
f很好形成,即使调用时,f可能会抛出异常Y是g不允许. - 末端的例子]
无论是什么促使决定,或者它可能是什么,似乎很清楚,这不是由于事故或疏忽造成的.
至于为何做出这个决定,至少有一些可以追溯到与C++ 11的其他新功能的交互,例如移动语义.
移动语义可以使异常安全(特别是强保证)更难以强制执行/提供.当你进行复制时,如果出现问题,很容易"回滚"交易 - 销毁你制作的任何副本,释放内存,原件保持不变.只有当/当复制成功时,才会销毁原件.
使用移动语义,这更难 - 如果你在移动物体中遇到异常,你已经移动的任何东西都需要移回原来恢复原始的顺序 - 但是如果移动构造函数或移动赋值运算符可以抛出,你可以在尝试移回东西以尝试恢复原始对象的过程中得到另一个异常.
将此与C++ 11可以/确实生成移动构造函数并为某些类型自动移动赋值运算符这一事实相结合(尽管存在一长串限制).这些并不一定能保证不会抛出异常.如果您明确地编写了一个移动构造函数,那么您几乎总是希望确保它不会抛出,这通常很容易做到(因为您通常"窃取"内容,您通常只是复制一些指针 - 容易做到没有例外).尽管如此,即使对于简单的模板,它也会变得更加困难std:pair.可以用需要复制的东西移动的一对东西变得难以处理.
这意味着,如果他们决定在编译时强制执行nothrow(和/或throw()),那么一些未知(但可能相当大)的代码将被完全破坏 - 多年来一直工作正常的代码突然不会甚至用新编译器编译.
除此之外,虽然它们没有被弃用,但动态异常规范仍然保留在语言中,因此它们最终会在运行时最终强制执行至少一些异常规范.
所以,他们的选择是:
我怀疑是否有人喜欢这些选择,但第三个看起来似乎是最后一个坏事.
一个原因很简单,编译时执行异常规范(任何风格)是一个痛苦的屁股.这意味着如果添加调试代码,则可能必须重写整个异常规范层次结构,即使您添加的代码不会抛出异常.当你完成调试后,你必须再次重写它们.如果你喜欢这种繁忙的工作,你应该用Java编程.
编译时检查的问题:它无法以任何有用的方式实现.见下一个例子:
void foo(std::vector<int>& v) noexcept
{
if (!v.empty())
++v.at(0);
}
Run Code Online (Sandbox Code Playgroud)
这段代码可以抛出吗?显然不是.我们可以自动检查吗?并不是的.Java这样做的方法是将主体放在try-catch块中,但我不认为它比我们现在拥有的更好......