简体   繁体   中英

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. No std::terminate is called.
  • gcc (6.3.0), stack in throw() function is unwound but then std::terminate is called (exception cannot be cought by try/catch). But with 7.0 (current HEAD), no stack is being unwound, std::termiante is called immediately. Actually gcc 7.0 even warns of this : warning: throw will always call terminate() [-Wterminate] for NoExceptFunctionWithObj2() . It makes throw() behave as noexcept(true).

  • clang, in all versions I have checked unwinds function stack (objects d-tors are called), and then std::terminate is called.

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. The latest published draft of the standard says :

15.4 [except.spec], paragraph 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 .

Which means that throw() is strictly equivalent to noexcept(true) . throw() is deprecated in C++17 .

15.5.1 [except.terminate]

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). 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

Not calling std::terminate means MSVC is not compliant.

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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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