Deprecated throw-list in C++11
Asked Answered
O

3

50

Just as I can see in cppreference, the classic "throw" declaration lists is now deprecated in C++11. What is the reason of leaving this mechanism and how should I have to specify what exceptions throws a function of mine?

Outrider answered 12/12, 2012 at 14:7 Comment(0)
C
56

For more detailed reasoning, see: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3051.html

As expressed in the national body comment above, exception specifications have not proven useful in practice. There are numerous discussions of the problems with exception specifications in C++ (see, e.g., [Sutter02], [Boost03]), but the main issues are:

  • Run-time checking: C++ exception specifications are checked at runtime rather than at compile time, so they offer no programmer guarantees that all exceptions have been handled. The run-time failure mode (calling std::unexpected()) does not lend itself to recovery.
  • Run-time overhead: Run-time checking requires the compiler to produce additional code that also hampers optimizations.
  • Unusable in generic code: Within generic code, it is not generally possible to know what types of exceptions may be thrown from operations on template arguments, so a precise exception specification cannot be written.

In practice, only two forms of exception-throwing guarantees are useful: an operation might throw an exception (any exception) or an operation will never throw any exception. The former is expressed by omitting the exception-specification entirely, while the latter can be expressed as throw() but rarely is, due to performance considerations.

[N3050] introduces a new kind of exception specification, noexcept, the specifies that the function will not throw any exceptions. Unlike throw(), noexcept does not require the compiler to introduce code to check whether an exception is thrown. Rather, if a function specified as noexcept is exited via an exception, the result is a call to std::terminate().

With the introduction of noexcept, programmers can now express the two kinds of exception guarantees that are useful in practice, without additional overhead. This paper therefore proposes to deprecate "dynamic" exception specifications, i.e., those that are written as throw(type-id-listopt).

Crafty answered 12/12, 2012 at 14:19 Comment(7)
Looking at [except.spec], note 9, in n3485 it seems that the dynamic check part has been later re-introduced: Whenever an exception is thrown and the search for a handler (15.3) encounters the outermost block of a function with an exception-specification that does not allow the exception, then, if the exception-specification is a dynamic-exception-specification, the function std::unexpected() is called (15.5.2), otherwise, the function std::terminate() is called (15.5.1). (emphasis mine); given the grammar of exception-specification, otherwise clause may only concern noexceptEntanglement
A sad side note is that noexcept is useless in practice, because what kind of help is it to terminate a program instead of trying to deal with failure to declare an error that was thought to be impossible but still happened. "If we (or somebody else) failed, just terminate" seems a terrible strategy for error-handling.Buttonball
@ChristopherOezbek it's a perfectly valid strategy, because you generally don't want your application to continue doing stuff if it's in a state you never thought possible, and therefore also didn't code for. It's better the application exits immediately, so you immediately see that something's wrong.Highchair
@Pezo: this is not correct. In most cases in production you need to continue somehow (restarting the process as the worst case). Immediate termination is the laziest approach for us engineers and never ideal.Buttonball
@ChristopherOezbek you would most certainly want your database server to crash (fail-fast) instead of corrupting your database, just as one example.Highchair
@Highchair I disagree and think its lazy for an engineer to concede defeat instead of fighting a failure to the end. Applications must be hardened to survive and properly treat errors as much as possible.Buttonball
Not to necro, but you gotta love how much pain people put themselves through to incorporate a language feature. The real conclusion is obvious: don't use exceptions. If you have read more material than the K&R book on a single feature just to not use it incorrectly, it's not worth using at all. We simply needed multiple return values.Carhop
B
10

The answer Peter gave does not hit the actual problem of exception specifications for the implementor and user:

  • Exception specifications cause the program to terminate (or more precisely call termination handlers) if the implementor failed to uphold the guarantee to only throw the defined exceptions.
  • Thus by calling a method with a exception specification you as a library user are making your own code more prone to complete failure/termination. If the library function runs out-of-memory (std::bad_alloc), you will not get a chance to catch, but you will be terminated instead.
  • Thus, the original goal of communicating the most likely failure options and asking you as the user to handle them was not achieved.
  • As an implementor on the other side, you cannot really call any other methods any more which do not have exception specifications, because these might cause you to terminate your callers. A terrible place to be in.

The conclusion is that C++ should have just gone the way that Java did it:

  • If you call a method with exception specification and you have an exception specification yourself, then you must catch the exception or specify it in your own exception specification.
  • The compiler enforces this and no other runtime effects.

Noexcept (since C++11) suffers the same conceptual mistake, since it will also cause run-time termination if the specification is not adhered too, i.e. a method throws that was declared not to. This makes noexcept impossible to use for anything serious but the most contained cases (move constructors come to mind).

Buttonball answered 11/3, 2017 at 21:59 Comment(2)
It's actually very simple to meet noexcept requirements. Just as we've had to do since C++ began with destructors which cannot throw, wrap it all in a try / catch(...) block and handle it before returning.Aconcagua
In fact this is one of the very few aspects in which Java is decidedly better than C++.Tove
B
5

They produce slower and bigger code, because libc++ has to check if any exception propagating out of a function violates it's exception specification and call std::unexpected. This is hardly ever useful, and is worse than just documenting the exceptions a function throws yourself.

Bifid answered 12/12, 2012 at 14:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.