简体   繁体   中英

How one can dynamic_cast from std::exception to std::nested_exception?

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.

The documentation for std::throw_with_nested states that the type thrown will publicly dervive from both std::nested_exception and the type of the exception you pass in. So, in your example, the exception thrown conceptually has the type:

class some_exception : public std::nested_exception, public std::runtine_exception
{

};

And, as std::runtime_exception is derived from std_exception you are able to catch it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM