【发布时间】:2012-12-12 14:07:00
【问题描述】:
正如我在cppreference 中看到的那样,经典的“抛出”声明列表现在在 C++11 中已弃用。离开这个机制的原因是什么?我应该如何指定哪些异常会引发我的函数?
【问题讨论】:
正如我在cppreference 中看到的那样,经典的“抛出”声明列表现在在 C++11 中已弃用。离开这个机制的原因是什么?我应该如何指定哪些异常会引发我的函数?
【问题讨论】:
更详细的推理见: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) 的规范。
【讨论】:
[except.spec],注9,在n3485中似乎动态检查部分后来被重新引入:每当抛出异常并搜索处理程序(15.3)遇到具有不允许异常的异常规范的函数的最外层块,则如果异常规范是动态异常规范,则调用函数std::unexpected() (15.5.2),否则,函数std::terminate() 被调用(15.5.1)。(强调我的);给定exception-specification的语法,otherwise子句可能只关注noexcept
noexcept 在实践中毫无用处,因为终止程序而不是尝试处理未能声明被认为不可能的错误有什么帮助但还是发生了。 “如果我们(或其他人)失败了,就终止”似乎是一种糟糕的错误处理策略。
Peter 给出的答案并没有触及实现者和用户的异常规范的实际问题:
结论是 C++ 应该走 Java 的路:
Noexcept (C++11 起) 存在同样的概念错误,因为如果不遵守规范也会导致运行时终止,即声明不遵守的方法抛出。这使得noexcept 无法用于任何严重的情况,但包含最多的情况 (move constructors come to mind)。
【讨论】:
它们会产生更慢和更大的代码,因为 libc++ 必须检查从函数传播的任何异常是否违反了它的异常规范并调用 std::unexpected。这几乎没有用,而且比仅仅记录一个函数自己抛出的异常更糟糕。
【讨论】: