This code:
#include <iostream>
#include <stdexcept>
using namespace std;
int throw_it() {
throw range_error( "foo" );
}
int main() {
try {
throw throw_it();
}
catch ( exception const &e ) {
cerr << e.what() << endl;
return 0;
}
}
prints foo
when run, but is it guaranteed to do so? More specifically, does throwing an exception while in the process of throwing an exception result in defined behavior? And is that behavior to throw the most-recently-thrown exception (as the test code above does)?
FYI:
$ g++ --version
i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
Only one exception can be evaluated at a time. From 15.1/7
If the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught, calls a function that exits via an exception, std::terminate is called.
In your example std::terminate()
is not called because only one exception is actually being thrown. When throw throw_it();
is reached, throw_it()
is evaluated first which results in the function being called before the exception is actually thrown. Since the function throws an exception and never returns the original throw
is never reached. If throw_it()
did not throw an exception and returned an integer value the calling throw expression would then be executed. So for your example foo
is guaranteed to be printed.
But what about throwing a new exception from within an active handler? When an exception is caught it has been fully evaluated. When you throw a new exception (rather than rethrowing with "throw;") the original exception is destroyed and evaluation of the new exception begins.
From 15.1/4
The exception object is destroyed after either the last remaining active handler for the exception exits by any means other than rethrowing or....
This satisfies the rule that only one exception can be evaluated at a time.
More specifically, does throwing an exception while in the process of throwing an exception result in defined behavior?
it is OK as long as it is not after constructing an exception object and before catching it, because then std::terminate
would be called. In your example exception object is not created at all, so there is nothing to be thrown, instead range_error("foo")
is thrown. This is exception to be catched.
You can inspect this this way:
int throw_it() {
throw range_error( "foo" );
return 19;
}
int main() {
try {
try { // nested try is perfectly legal
throw throw_it();
} catch (...) {
cerr << "not good!";
}
} catch (exception const &e) {
cerr << e.what() << endl;
return 0;
}
}
output:
not good!
RUN SUCCESSFUL (total time: 59ms)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With