26

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

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

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

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

顺便说一句,如果您问为什么没有第三个选项(也就是不能完成),原因是我可以很容易地想到一种(慢)方法来检查函数是否可以抛出。如果您将输入限制为 5 和 7(也就是我保证该文件不会包含除 5 和 7 之外的任何内容)并且它只会在您给它 33 时抛出,那么问题就出来了,但恕我直言,这不是一个现实的问题。

4

4 回答 4

24

委员会非常清楚地考虑了(试图)抛出异常规范不允许的异常的代码被认为格式错误的可能性,并拒绝了这个想法。根据 15.4 美元/11 美元:

实现不应仅仅因为表达式在执行时抛出或可能抛出包含函数不允许的异常而拒绝表达式。[ 例子:

 extern void f() throw(X, Y);

 void g() throw(X) {
      f(); // OK
  }

调用f格式正确,即使在调用时可能会f抛出不允许的异常。—结束示例]Yg

不管是什么促成了这个决定,或者它可能是什么,很明显这不是事故或疏忽的结果。

至于为什么做出这个决定,至少有一些可以追溯到与 C++11 的其他新特性的交互,例如移动语义。

移动语义可以使异常安全(尤其是强保证)更难执行/提供。当你进行复制时,如果出现问题,很容易“回滚”事务——销毁你所做的任何副本,释放内存,原始数据保持不变。仅当/当复制成功时,您才销毁原件。

使用移动语义,这更难——如果你在移动东西的过程中遇到异常,你已经移动的任何东西都需要移原来的位置以恢复原来的顺序——但是如果移动构造函数或移动赋值运算符可以抛出,你可能会在尝试将事物移回以尝试恢复原始对象的过程中得到另一个异常。

将此与 C++11 可以/确实为某些类型自动生成移动构造函数和移动赋值运算符的事实相结合(尽管有很长的限制列表)。这些不一定保证不会引发异常。如果您明确编写移动构造函数,您几乎总是希望确保它不会抛出,这通常甚至很容易做到(因为您通常“窃取”内容,您通常只是复制一些指针 -容易做到没有例外)。但是,即使对于像std:pair. 一对可以移动的东西和需要复制的东西变得很难处理好。

这意味着,如果他们决定在编译时强制执行nothrow(和/或throw()),一些未知(但可能相当大)数量的代码将被完全破坏——多年来一直运行良好的代码突然不会甚至用新的编译器编译。

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

所以,他们的选择是:

  1. 破坏大量现有代码
  2. 限制移动语义,以便它们适用于更少的代码
  3. 继续(如在 C++03 中)在运行时强制执行异常规范。

我怀疑有人喜欢这些选择中的任何一个,但第三个显然似乎是最后一个不好的选择。

于 2013-01-29T22:48:55.183 回答
11

一个原因很简单,异常规范(任何风格)的编译时强制执行是一件令人头疼的事情。这意味着如果您添加调试代码,您可能必须重写整个异常规范层次结构,即使您添加的代码不会引发异常。当你完成调试时,你必须再次重写它们。如果您喜欢这种忙碌的工作,您应该使用 Java 进行编程。

于 2013-01-30T15:06:48.837 回答
8

编译时检查的问题:实际上不可能以任何有用的方式进行。请参见下一个示例:

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

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

于 2013-07-13T08:41:58.597 回答
4

据我了解(诚然有些模糊),当真正尝试以有用的方式使用它时,发现抛出规范的整个想法是一场噩梦。

调用没有指定它们抛出或不抛出什么的函数必须被认为可能会抛出任何东西!所以编译器,如果它要求你既不抛出也不调用任何可能抛出任何超出你提供的规范的东西实际上强制执行这样的事情,你的代码几乎不能调用任何东西,现有的库将没有任何用处给您或任何其他试图使用 throw 规范的人。

而且由于编译器不可能区分“这个函数可能会抛出一个 X,但调用者很可能会以一种根本不会抛出任何东西的方式调用它”——人们将永远被束缚这种语言的“特征”。

所以......我相信它唯一可能有用的事情是说 nothrow 的想法 - 这表明从 dtors 调用并移动和交换等是安全的,但是你正在做一个符号- 像 const - 更多的是给你的用户一个 API 合约,而不是让编译器负责判断你是否违反了你的合约(就像大多数 C/C++ 一样 - 智能被认为是程序员的一部分,而不是保姆编译器)。

于 2013-07-12T19:06:27.947 回答