为什么noexcept在编译时没有强制执行?

时间:2013-01-29 22:37:20

标签: c++ c++11

您可能知道C ++ 11有noexcept关键字。现在丑陋的部分就是这个:

  

请注意,函数的noexcept规范不是编译时   校验;它只是程序员通知编译器的一种方法   函数是否应该抛出异常。

http://en.cppreference.com/w/cpp/language/noexcept_spec

这是委员会部分的设计失败还是他们只是把它作为编译作者的练习:)从某种意义上来说,正常的编译器会强制执行它,坏的仍然可以兼容吗?

如果你问为什么没有第三种选择(也就是说无法完成),那么理由是我可以很容易地想到一种(慢)方法来检查函数是否可以抛出。如果你将输入限制为5和7(也就是我保证文件不会包含5和7旁边的任何内容),那么问题就会偏离当然,它只会在你给它33时抛出,但这不是一个现实的问题恕我直言。

4 个答案:

答案 0 :(得分:20)

委员会非常清楚地认为,(试图)抛出异常规范不允许的异常的代码被认为是不正确的,并且拒绝了这个想法。根据$ 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(),那么一些未知(但可能相当大)的代码将完全被破坏 - - 多年来一直运行良好的代码突然甚至无法使用新的编译器进行编译。

除此之外,尽管它们并未被弃用,但动态异常规范仍然保留在语言中,因此它们最终会在运行时最终强制执行至少一些异常规范。

所以,他们的选择是:

  1. 打破很多现有代码
  2. 限制移动语义,以便它们适用于少得多的代码
  3. 继续(如在C ++ 03中)在运行时强制执行异常规范。
  4. 我怀疑是否有人喜欢这些选择中的任何一种,但第三种看起来似乎是最后的不好。

答案 1 :(得分:9)

一个原因很简单,编译时执行异常规范(任何风格)都是一种痛苦。这意味着如果添加调试代码,则可能必须重写整个异常规范层次结构,即使您添加的代码不会抛出异常。当你完成调试后,你必须再次重写它们。如果你喜欢这种繁忙的工作,你应该用Java编程。

答案 2 :(得分:6)

编译时检查的问题:它无法以任何有用的方式实现。 见下一个例子:

void foo(std::vector<int>& v) noexcept
{
    if (!v.empty())
        ++v.at(0);
}

此代码可以抛出吗? 显然不是。我们可以自动检查吗?并不是的。 Java这样做的方法是将主体放在try-catch块中,但我不认为它比我们现在拥有的更好......

答案 3 :(得分:4)

正如我所理解的那样(当然有点模糊),当实际上是时候尝试以有用的方式使用它时,发现抛出规格的整个想法是一场噩梦。

调用不指定抛出或不抛出的函数必须考虑可能抛出任何东西!那么编译器,是否要求你既不抛出也不调用任何可能抛出你所提供的规范之外的东西实际上强制执行这样的事情,你的代码几乎什么都不会调用,没有任何库会有任何用处对你或任何其他试图使用投掷规范的人。

并且因为编译器不可能区分“这个函数可能会抛出一个X,但是调用者可能会以这样的方式调用它,它永远不会抛出任何东西” - 一个人会永远被这种语言“特征”所困扰。

所以...我相信唯一可能有用的东西就是说不要说 - 这表明从dtors调用并移动和交换等等是安全的,但是你正在制作一个符号 - 就像const一样 - 更多的是给你的用户一个API合同,而不是让编译器负责判断你是否违反了你的合同(就像大多数事情C / C ++一样 - 假设情报部分是这样的程序员,而不是保姆编译器。