0

From man gcc:

  -fno-enforce-eh-specs
      Don't generate code to check for violation of exception specifications
      at run time.  This option violates the C++ standard, but may be useful
      for reducing code size in production builds.

When a compiler optimizes, it removes all sorts of checks, it breaks the boundaries between functions, it might even avoid things like system calls the developer put in the code in some cases. So:

  • Why is exception specification violation checking so special that it cannot be skipped?
  • What exactly is being checked?
  • If using this switch is so useful for reducing code size, how come the C++ standard requires these checks? I thought the idea is zero-overhead abstractions whenever possible.
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • Optimizations are allowed to do whatever they want as long as the *observable behavior* does not change. Dropping checks for exception specifications does change the behavior as an incorrect exception being throw is mandated to call `std::terminate()` which won't happen if the type of exceptions isn't checked but a wrong exception actually *is* thrown. – Dietmar Kühl Dec 25 '16 at 22:46
  • @DietmarKühl: It won't change if the function doesn't actually throw any forbidden exceptions... but I guess I see what you mean. – einpoklum Dec 25 '16 at 22:48

2 Answers2

4

The (now old?) standard requires that a function declared void f() throw(x); must not throw any exception other than x (or possibly derived from x?). If it tries to throw something else, std::unexpected() should be called, and probably end up in a call to std::terminate() to kill the program.

But if the exact type of the exception cannot be determined at compile time, a run-time check might be needed to determine if the exception is of an acceptable type.

Of course, if that check is removed by this option, an incorrect exception might escape from the function. And that would be a violation of the standard.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • So you're saying this restriction is made irrelevant by C++17? – einpoklum Dec 25 '16 at 22:41
  • The same rules still apply to `noexcept`: if a `noexcept` function throws an exception, the standard requires that to be handled similarly; that too requires code to potentially call `std::terminate` (rather than `std::unexpected`). And `noexcept` is very much still recommended. The code cost may be smaller than with `throw(x)`, since there's no exception type checking involved, but the cost is still not zero. –  Dec 25 '16 at 22:44
2

Why is exception specification violation checking so special that it cannot be skipped?
What exactly is being checked?

This has been answered already, but for completeness in my answer as well: for functions with throw(...) and noexcept specifications, when exceptions get thrown that are not permitted, std::unexpected() or std::terminate() must be called.

Effectively, they turn something like

void f();
void g() noexcept { f(); }

into something very similar to:

void f();
void g() { try { f(); } catch (...) { std::terminate(); } }

If using this switch is so useful for reducing code size, how come the C++ standard requires these checks? I thought the idea is zero-overhead abstractions whenever possible.

Indeed. But there is no way to implement this without overhead. Compilers do already manage to implement exceptions in such a way that the code that gets executed at run-time, as long as no exceptions are thrown, is identical to the version without exception handling. In that sense, on platforms with suitable ABIs, zero-overhead exceptions has been achieved. They, however, do still annotate that code with special markers that lets the run-time library figure out what code to call when exceptions do end up thrown. Those special markers take up space. The code that ends up interpreting those markers takes up space as well. And the actual exception handling code, even if it's only std::terminate(), takes up yet more space.