简体   繁体   English

throw()函数是否应该始终在异常上展开堆栈并允许捕获异常或必须调用std :: terminate?

[英]Should throw() function always unwind stack on exception and allow exception to be catched or std::terminate must be called?

I am interested whether this is forced by standard and whether it is violated by some compiler. 我很感兴趣这是否是由标准强制执行的,以及是否被某些编译器所违反。 My observation is that: 我的观察是:

  • Visual Studio 2015/2017 (with /EHsc): stack in the throw() function is not unwound (d-tors are not called), but exception exits function and is being caught by try/catch. Visual Studio 2015/2017(带有/ EHsc):throw()函数中的堆栈未展开(未调用d-tors),但是异常退出了功能并被try / catch捕获。 No std::terminate is called. 没有调用std :: terminate。
  • gcc (6.3.0), stack in throw() function is unwound but then std::terminate is called (exception cannot be cought by try/catch). gcc(6.3.0),未取消throw()函数中的堆栈,但随后调用了std :: terminate(try / catch无法解决异常)。 But with 7.0 (current HEAD), no stack is being unwound, std::termiante is called immediately. 但是使用7.0(当前HEAD)时,没有取消堆栈堆栈,因此立即调用std :: termiante。 Actually gcc 7.0 even warns of this : warning: throw will always call terminate() [-Wterminate] for NoExceptFunctionWithObj2() . 实际上,gcc 7.0甚至对此发出警告: warning: throw will always call terminate() [-Wterminate]NoExceptFunctionWithObj2() warning: throw will always call terminate() [-Wterminate] It makes throw() behave as noexcept(true). 它使throw()的行为与noexcept(true)相同。

  • clang, in all versions I have checked unwinds function stack (objects d-tors are called), and then std::terminate is called. 在所有版本中,我都检查过展开函数堆栈(调用了对象d-tors),然后调用了std :: terminate。

Test code: 测试代码:

#include <iostream>
#include <string>
#include <vector>

struct TestDataWithoutNoexcept {
  TestDataWithoutNoexcept() {
    std::cout << __FUNCTION__ << "\n";
  }
  ~TestDataWithoutNoexcept() {
    std::cout << __FUNCTION__ << "\n";
  }
  TestDataWithoutNoexcept(TestDataWithoutNoexcept const & rhs) {
    std::cout << __FUNCTION__ << "\n";
  }
  TestDataWithoutNoexcept(TestDataWithoutNoexcept && rhs) {
    std::cout << __FUNCTION__ << "\n";
  }
  TestDataWithoutNoexcept& operator=(TestDataWithoutNoexcept const& rhs) {
    std::cout << __FUNCTION__ << "\n";
  }
};

void NoExceptFunctionWithObj1() noexcept {
  TestDataWithoutNoexcept test;
  throw std::runtime_error("NoExceptFunctionWithObj1 ex.");
}

void NoExceptFunctionWithObj2() throw() {
  TestDataWithoutNoexcept test;
  throw std::runtime_error("NoExceptFunctionWithObj2 ex.");
}

int main()
{
  // Now lets see whether stack is being unwound when exception is thrown in noexcept versus throw() function.
  std::cout << "\n See how dtors are called in noexcept or throw() functions\n";
  try {
    //NoExceptFunctionWithObj1();
  }
  catch (std::runtime_error& ex) {
    std::cout << ex.what();
  }

  try {
    NoExceptFunctionWithObj2();
  }
  catch (std::runtime_error& ex) {
    std::cout << "\nShouldn't this be shown? : " << ex.what();
  }
}

Yes, std::terminate should be called. 是的,应该调用std::terminate The latest published draft of the standard says : 最新发布的标准草案说:

15.4 [except.spec], paragraph 12 15.4 [规格除外],第12段

An exception-specification is non-throwing if it is of the form throw() , noexcept , or noexcept( constant-expression ) where the constant- expression yields true . 如果异常规范的形式为throw(),noexcept或noexcept(constant-expression),其中常量表达式产生true,则该异常规范为非抛出。

Which means that throw() is strictly equivalent to noexcept(true) . 这意味着throw()严格等同于noexcept(true) throw() is deprecated in C++17 . C++17不推荐使用throw()

15.5.1 [except.terminate] 15.5.1 [终止除外]

when the search for a handler (15.3) encounters the outermost block of a function with a noexcept- specification that does not allow the exception (15.4) [...] std::terminate() is called (18.8.3). 在处理程序(15.3)的搜索遇到带有noexcept-spec的函数的最外面的块时,该函数不允许出现异常(15.4)[...] std :: terminate()(18.8.3)。 In the situation where no matching handler is found, it is implementation-defined whether or not the stack is unwound before std::terminate() is called 在找不到匹配处理程序的情况下,由实现定义是否在调用std :: terminate()之前取消堆栈的堆栈

Not calling std::terminate means MSVC is not compliant. 不调用std::terminate表示MSVC不兼容。

Regarding the handling of the stack, the compiler does what it wants about unwinding it or not in your example — this is specified to be implementation-defined . 关于堆栈的处理,在您的示例中,编译器会执行它希望对其进行展开或不展开的操作-这被指定为实现定义的

Historically (before C++11) stack unwinding in this situation was mandatory. 历史上(在C ++ 11之前)在这种情况下必须进行堆栈展开。 However it turned out that the runtime cost of this enforced behaviour was too high and it was inhibiting the compiler to do some optimizations (even in the non-exception case). 然而,事实证明,这种强制行为的运行时成本太高,并且它抑制了编译器进行某些优化(即使在非异常情况下)。 As a result, the compiler is now free to leave this out. 结果,编译器现在可以自由地将其省略。

Edited after clarifications from @mike. 经过@mike澄清后进行编辑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM