1

问题是关于 C++ 的历史:ISO 标准、修订标准(带有 DR),甚至是标准草案;都算作“C++”。

是否有任何 C++ 不具备此属性

使用空抛出规范声明的函数throw()不能抛出异常。

如果该属性不成立,我想要一个反例。

注释:

不言而喻,在函数抛出异常,然后捕获(不重新抛出)异常,是否会将其转换为“抛出异常的函数”。根据其规范,抛出异常的函数会向其调用者抛出异常。(如果您在内部进行操作,则根据定义,它不是规范的一部分。)

[假设这longjmp是被禁止的,因为我们有可破坏的物体。]

该问题的其他等效表达是:

是否有任何情况下声明的无抛出函数的调用者不得不担心这种可能性(等效属性):

  • 该函数将控制权交还给其调用者,但不使用 return 语句
  • 查看(并能够捕获)该函数抛出的异常
  • 由于调用该函数而导致堆栈展开
  • 如果所有其他操作(调用该函数除外)都是非抛出的,则调用者不是非抛出的

换句话说,是否有任何 C++编译器无法根据看到被调用函数的无抛出声明来优化调用函数?

4

2 回答 2

3

C++17 主要throw作为函数注释已被弃用。它仍然允许throw()并认为它等同于noexcept(true). 这意味着这throw()表明函数不应该因为异常而退出。违反noexcept(true)保证会导致未定义的行为。

C++17 之前的语义throw()是截然不同的。曾经承诺,::std::unexpected()如果throw()违反该子句(即抛出异常并导致函数退出),将发生调用。

因此,这意味着在 C++17 之前,编译器仍然需要一些机制来检测函数没有从异常中退出,即使它被注释为throw(). 这是throw(...)函数注释(括号之间有东西)被弃用的原因之一,因为在不删除注释具有的其他用途的情况下必须throw()更改为等效是没有意义的。noexcept(true)throw

编译器资源管理器中的这段代码演示了“意外异常”处理程序。如果您将要求的标准更改为 C++17,您将看到该代码消失。

extern void a_different_function();

void test_function() throw ()
{
    a_different_function();
}
于 2019-10-28T06:21:13.403 回答
1

换句话说,是否有任何 C++ 编译器无法根据看到被调用函数的无抛出声明来优化调用函数?

这个直接问题的答案是否定的。但仅此一项就具有很大的误导性。

编译器对碰巧调用具有throw()声明的其他函数的函数进行任何类型的优化的能力极为有限。编译器唯一能做的就是消除在调用函数中处理异常的任何代码的发射。但是由于所述代码的性质,它实际上只有在它调用的每个函数都没有抛出时才适用。就调用函数的函数的编译器优化而言,几乎就是这样throw()

今天人们经常谈论如何noexcept实现优化。这是真的;明智地使用noexcept可以使在这些函数上运行的代码变得更有效率。但重要的是要记住 usingnoexcept不会启用编译器优化;它支持用户代码优化。

让我们以带有移动支持的vector<T>for a的经典案例为例。这种情况并没有更快,因为编译器会看到本来应该是一系列副本的内容,并自动将它们更改为 move ,因为 move 构造函数是. 这对编译器来说甚至是不可能的;不允许像那样重新排列您的代码,因为这将是一个可检测的更改,具体取决于您的复制/移动构造函数的操作。Tnoexceptnoexcept

vector这种情况会变得更快,因为检测到的源代码实现T是不可移动的。检测到这种情况后,它将调用一个完全不同的代码路径来改组vector周围的元素。调用非抛出函数的代码变得更快是因为调用代码优化了自身,而不是因为编译器检测到任何东西。

简而言之,调用函数的编译器优化从来都不是throw()声明的重点。

于 2019-10-29T15:32:16.877 回答