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?
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).
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 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).
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.
© 2022 - 2024 — McMap. All rights reserved.
[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 functionstd::unexpected()
is called (15.5.2), otherwise, the functionstd::terminate()
is called (15.5.1). (emphasis mine); given the grammar of exception-specification, otherwise clause may only concernnoexcept
– Entanglement