Where does the object that is "thrown" by the throw statement
live? For example, if I write this code:
if (something_went_wrong)
{
my_exception_class x;
x.set_text("Something went wrong");
x.set_error_number(123);
x.set_something_else(true);
I would expect (based on very limited experience with
try/catch/throw) that "x" would be created on the stack.
"x" is (unless some optimizations occur).
But the documentation for try/catch/throw says that the stack
is "unwound" when the throw statement is executed, and that
objects on the stact are destructed. So "x" would cease to
exist, wouldn't it?
Yes.
And the catch block would not "catch" anything at all?
It catches a copy that was made before x was destructed.
Or does the thing that is "thrown" get special treatment by
the compiler?
The formal semantic of a throw is that the thrown object (which
can be a local variable or a temporary) will be copied
"somewhere" where it won't be destructed until it should be.
From the standard:
A throw-expression initializes a temporary object,
called the exception object[...] The temporary is an
lvalue and is used to initialize the variable named in
the matching handler.[...]
The memory for the temporary copy of the exception being
thrown is allocated in an unspecified way, except as
noted in 3.7.4.1. The temporary persists as long as
there is a handler being executed for that exception. In
particular, if a handler exits by executing a throw;
statement, that passes control to another handler for
the same exception, so the temporary remains. When the
last remaining active handler for the exception exits by
any means other than throw; the temporary object is
destroyed and the implementation may deallocate the
memory for the temporary object; any such deallocation
is done in an unspecified way. The destruction occurs
immediately after the destruction of the object declared
in the exception-declaration in the handler.
I'm not too happy with the first sentence above. In most other
contexts of the standard, "temporary object" has a very definite
meaning, different from what is meant here. The important
things to note, however is that 1) it is a copy that is thrown
(although the compiler can optimize the copy out in certain
specific cases), and 2) the object actually thrown has a
specific lifetime, unrelated to any of the other lifetimes the
standard defines (and I guess 3) the rest is the compiler's
problem, not yours).
You might want to try something like:
#include <iostream>
#define TRACE( what ) std::cout << #what ", this = " << this <<
std::endl
class X
{
public:
X() { TRACE( ctor ) ; }
X( X const& ) { TRACE( copy ) ; }
X& operator=( X const& ) { TRACE( asgn ) ; return *this ; }
~X() { TRACE( dtor ) ; }
} ;
void
f()
{
std::cout << "in f()" << std::endl ;
X localX ;
std::cout << "before throw" << std::endl ;
throw localX ;
}
int
main()
{
try {
f() ;
} catch ( X const& x ) {
std::cout << "in catch, &x = " << &x << std::endl ;
}
return 0 ;
}
to see exactly what your implementation does.