Reputation: 3207
According to Optimizing C++,
Use the empty exception specification (that is, append throw() to the declaration) for the functions you are sure will never throw exceptions.
What if I know that 90% of my methods won't throw an exception? It seems unconventional and verbose to append throw() to all of those methods. If not, what are the advantages? Or am I misunderstanding something here?
Upvotes: 5
Views: 1013
Reputation: 340406
The standard throw()
doesn't enhance optimizability.
If a method is marked as throw()
then the compiler is forced to check if an exception is thrown from the method and unwind the stack - just like if the function is not marked as throw()
. The only real difference is that for a function marked throw()
the global unexpected_handler
will be called (which generally calls terminate()
) when the exception leaves the function, unwinding the stack to that level, instead of the behavior for functions without an exception specification which will handle the exception normally.
For pre-C++11 code, Sutter & Alexandrescu in "C++ Coding Standards" suggested:
Avoid exception specifications.
Take exception to these specifications: Don’t write exception specifications on your functions unless you’re forced to (because other code you can’t change has already introduced them; see Exceptions).
...
A common but nevertheless incorrect belief is that exception specifications statically guarantee that functions will throw only listed exceptions (possibly none), and enable compiler optimizations based on that knowledge
In fact, exception specifications actually do something slightly but fundamentally different: They cause the compiler to inject additional run-time overhead in the form of implicit try/catch blocks around the function body to enforce via run-time checking that the function does in fact emit only listed exceptions (possibly none), unless the compiler can statically prove that the exception specification can never be violated in which case it is free to optimize the checking away. And exception specifications can both enable and prevent further compiler optimizations (besides the inherent overhead already described); for example, some compilers refuse to inline functions that have exception specifications.
Note that in some versions of Microsoft's compilers (I'm not sure if this behavior has changed in more recent versions, but I don't think so), throw()
is treated in a non-standard way. throw()
is equivalent to __declspec(nothrow)
which does allow the compiler to assume that the function will not have an exception thrown and undefined behavior will result if one is.
C++11 deprecates the C++98 style exception specification and introduced the noexcept
keyword. Bjarne Stroustup's C++11 FAQ says this about it:
If a function declared noexcept throws (so that the exception tries to escape, the noexcept function) the program is terminated (by a call to terminate()). The call of terminate() cannot rely on objects being in well-defined states (i.e. there is no guarantees that destructors have been invoked, no guaranteed stack unwinding, and no possibility for resuming the program as if no problem had been encountered). This is deliberate and makes noexcept a simple, crude, and very efficient mechanism (much more efficient than the old dynamic throw() mechanism).
In C++11 if an exception is thrown from a function marked as noexcept
the compiler is not obligated to unwind the stack at all. This affords some optimization possibilities. Scott Meyers discusses the new noexcept
in his forthcoming book "Effective Modern C++".
Upvotes: 5
Reputation: 5856
C++11 has introduced noexcept
, throw
is somewhat deprecated (and according to this less efficient)
noexcept is an improved version of throw(), which is deprecated in C++11. Unlike throw(), noexcept will not call std::unexpected and may or may not unwind the stack, which potentially allows the compiler to implement noexcept without the runtime overhead of throw().
When an empty throw specification is violated, your program is terminated; this means you should only declare your functions as non throwing, only when they have a no throw exception guarantee.
Finally you need a move constructor to be non throwing (specified with noexcept
) to be able to use the r-value ref version of std::vector<T>::push_back
(see a better explanation here)
Upvotes: 9