48

正如我在 cppreference中看到的那样,经典的“抛出”声明列表现在在 C++11 中已弃用。离开这种机制的原因是什么,我应该如何指定哪些异常会引发我的功能?

4

3 回答 3

53

更详细的推理参见:http ://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3051.html

正如上述国家机构评论中所表达的那样,例外规范在实践中并未被证明是有用的。有很多关于 C++ 中异常规范问题的讨论(例如,参见 [Sutter02]、[Boost03]),但主要问题是:

  • 运行时检查:C++ 异常规范是在运行时而不是在编译时检查的,因此它们不提供程序员保证所有异常都已被处理的保证。运行时故障模式(调用 std::unexpected())不利于恢复。
  • 运行时开销:运行时检查需要编译器生成额外的代码,这也会妨碍优化。
  • 在泛型代码中不可用:在泛型代码中,通常不可能知道对模板参数的操作可能会引发哪些类型的异常,因此无法编写精确的异常规范。

在实践中,只有两种形式的异常抛出保证是有用的:一个操作可能抛出一个异常(任何异常),或者一个操作永远不会抛出任何异常。前者通过完全省略异常规范来表达,而后者可以表达为 throw() 但出于性能考虑,很少使用。

[N3050] 引入了一种新的异常规范,noexcept,指定函数不会抛出任何异常。与 throw() 不同的是,noexcept 不需要编译器引入代码来检查是否抛出异常。相反,如果指定为 noexcept 的函数通过异常退出,结果是调用 std::terminate()。

随着 noexcept 的引入,程序员现在可以表达两种在实践中有用的异常保证,而无需额外的开销。因此,本文建议弃用“动态”异常规范,即那些写为 throw(type-id-listopt) 的规范。

于 2012-12-12T14:19:02.803 回答
9

彼得给出的答案并没有解决实现者和用户的异常规范的实际问题:

  • 如果实现者未能坚持只抛出定义的异常的保证,异常规范会导致程序终止(或更准确地说是调用终止处理程序)。
  • 因此,通过调用具有异常规范的方法,您作为库用户正在使您自己的代码容易完全失败/终止。如果库函数内存不足(std::bad_alloc),您将没有机会捕获,但您将被终止。
  • 因此,传达最可能的失败选项并要求您作为用户处理它们的最初目标没有实现。
  • 作为另一方面的实现者,你不能真正调用任何其他没有异常规范的方法,因为这些可能会导致你终止调用者。一个可怕的地方。

结论是 C++ 应该像 Java 那样走:

  • 如果你调用一个带有异常规范的方法并且你自己有一个异常规范,那么你必须捕获异常或者在你自己的异常规范中指定它。
  • 编译器会强制执行此效果,但不会执行其他运行时效果。

Noexcept (C++11 起) 存在同样的概念错误,因为如果不遵守规范也会导致运行时终止,即声明不遵守的方法抛出。这使得noexcept不可能用于任何严重但包含最多的情况(想到移动构造函数)。

于 2017-03-11T21:59:19.313 回答
5

它们产生更慢和更大的代码,因为 libc++ 必须检查从函数传播的任何异常是否违反了它的异常规范并调用std::unexpected. 这几乎没有用,而且比仅仅记录一个函数自己抛出的异常更糟糕。

于 2012-12-12T14:17:31.797 回答