Throwing exception that has const reference to local variable

805 views Asked by At

What happens to the local variables during stack unwinding, that are referenced in exception? Consider following code:

class bar;
class my_error
{
public:
    my_error(const bar& bar) : _bar(bar) {}
    const bar& get_bar() const { return _bar; }

private:
    const bar& _bar;
}


...

bar some_local_object(...);
if (!foo()) {
   throw my_error(some_local_object);
}

...

try {
    g();
} catch (my_error& e) {
    e.get_bar()
    ...
}

What happens to some_local_object? Shouldn't it be destroyed during stack unwinding? Is it safe to use it, as provided in example?

Additional question

As already answered, this code would lead to undefined behavior. My second question to it is:

If I am neither allowed to pass reference to local object nor should I try to make copy of it, because in rare case it could cause bad_alloc (which is why, I guess, gcc standard library has no meaningful error message, i.e. map.at throws exception for which what() returns "map.at"), then what is a good strategy to pass additional information? Note that even joining multiple strings, during construction of error message could theoretically cause bad_alloc. i.e.:

void do_something(const key& k, ....)
{
    ...
    if (!foo(k)) {
        std::ostringstream os;
        os << "Key " << k << " not found"; // could throw bad_alloc
        throw std::runtime_error(os.str()); 
    }
    // another approcach
    if (!foo(k)) {
        throw key_not_found(k); // also bad, because exception could outlive k
    }
}
2

There are 2 answers

7
Dietmar Kühl On

The behavior is the same as when returning a reference to a variable on the stack: the object is destroyed before you get to use it. That is, by the time the exception is caught, the referenced object is destroyed and all access to the reference result in undefined behavior.

The relevant clause in the standard is 15.2 [except.ctor] paragraph 1:

As control passes from the point where an exception is thrown to a handler, destructors are invoked for all automatic objects constructed since the try block was entered. The automatic objects are destroyed in the reverse order of the completion of their construction.

0
Cheers and hth. - Alf On

The local object is destroyed during stack unwinding. The reference then becomes invalid, a dangling reference. Which means that inspection of the exception object such that the reference is used, will have Undefined Behavior.