I've just seen a code containing dynamic_cast from std::exception
to std::nested_exception
, for instance,
try {
std::throw_with_nested(std::runtime_error("error"));
} catch (std::exception &e) {
auto &nested = dynamic_cast<std::nested_exception&>(e);
std::cout << "ok" << std::endl;
}
At the very first time, I thought this code won't be compiled because std::nested_exception
is not derived from std::exception
and I expected dynamic_cast
would do static check of inheritance but I was wrong.
Although I couldn't find related standard specification which explicitly mentions that dynamic_cast
allows this, I confirmed that all three major compilers(clang/gcc/msvc) allow dynamic_cast
between totally unrelated types.
But still, std::nested_exception
is not derived from std::exception
, so I thought the dynamic_cast
will throw an bad_alloc
exception and "ok"
never printed. I was wrong again.
Now, I'm wondering how this can work. Is this a something special and exceptional for std::exception
and std::nested_exception
? Or, can I make another successful dynamic_cast<A&>(b)
where type A
and type of object b
do not have common base class?
At the very first time, I thought this code won't be compiled because std::nested_exception is not derived from std::exception
That's not sufficient - std::nested_exception
is intended to be used as a mixin class, like
struct MyExceptionWrapper: public std::exception, std::nested_exception
{
// an 3rd-party component of my library threw
// and I want to wrap it with a common interface
};
expected dynamic_cast would do static check of inheritance but I was wrong
In the above case dynamic_cast
has to check at runtime whether your std::exception
is really a MyExceptionWrapper
, in which case it is also a std::nested_exception
.
This is why it is called dynamic cast, because it has to check the dynamic type at runtime. If you want to perform a static check at compile time, you're looking for static cast.
Although I couldn't find related standard specification which explicitly mentions that dynamic_cast allows this
This is all well documented. We're discussing the following clause from the linked page:
- 5) If expression is a pointer or reference to a polymorphic type Base, and new_type is a pointer or reference to the type Derived a run-time check is performed:
(note that Base=std::exception
is polymorphic)
- b) Otherwise, if expression points/refers to a public base of the most derived object, and, simultaneously, the most derived object has an unambiguous public base class of type Derived, the result of the cast points/refers to that Derived (This is known as a "sidecast".)
Since you can't tell at compile time that a std::exception&
is not really a MyExceptionWrapper
, you have to do this at runtime.
PS. if you want to avoid accidentally rethrowing inside a catch
block, just write
auto *nested = dynamic_cast<std::nested_exception*>(&e);
instead. Then you can check for nullptr
to find out whether it succeeded.
PPS. As Sean suggests, the MyExceptionWrapper
above is really more likely to be a type generated by throw_with_nested
, but it has the same effect.