2

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();
  }
}
mike
  • 1,670
  • 11
  • 21
  • Is there a reason why you're only tagging this as C++11? And not C++14 or C++17? C++11 certainly is [relevant](http://stackoverflow.com/questions/13841559/deprecated-throw-list-in-c11) but C++17 has more changes. – MSalters Apr 12 '17 at 08:02
  • No reason - I will add add tags. Or maybe simply [c++] should suffice? – mike Apr 12 '17 at 08:12

1 Answers1

3

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

King Thrushbeard
  • 869
  • 10
  • 16
Jean-Bernard Jansen
  • 7,544
  • 2
  • 20
  • 17
  • 2
    is it really the latest published draft? C++14 is n4140. I have found here: http://eel.is/c++draft/except.terminate#2 (generated on 2017-04-08) that stack unwiding is implementation defined. And here: http://eel.is/c++draft/except.spec#2 that `The noexcept-specifier throw() is deprecated, and equivalent to the noexcept-specifier noexcept(true).` So in the end std::terminate have to be called, but whether function local objects get destroyed is up to the implementation. – mike Apr 12 '17 at 10:14
  • 7
    As a side note: [Visual Studio is explicitly non-compliant](https://msdn.microsoft.com/en-us/library/wfa0edys.aspx) with regards to violated `throw()` specifications, effectively sending you straight to undefined-behavior-land. That is, the compiler always assumes that a a function with empty exception spec will never throw. This has been the case in all versions of Visual Studio afaik. – ComicSansMS Apr 12 '17 at 10:55
  • @ComicSansMS : Thx for the additional info. – Jean-Bernard Jansen Apr 12 '17 at 13:01
  • @mike: Your are absolutely right, I just did not read enough of the paragraph to reach this part. We effectively read the same document, my quotes are the sentences right before yours ;) – Jean-Bernard Jansen Apr 12 '17 at 13:01