I have some remarks on the proposed compile-time x runtime solutions. In addition to when they are evaluated, is_base_of
and dynamic_cast
have different requirements and their answers can be different.
(1) First of all (as pointed out by others) to use dynamic_cast
, base and derived classes must be polymorphic (must have at least one virtual
method). is_base_of
doesn't require the types to be polymorphic.
(2) The operands of is_base_of
are both types whereas dynamic_cast
needs a type (inside < >
) and an object (inside ( )
).
(3) dynamic_cast
and is_base_of
can give different answers (or one can compile while the other doesn't) depending on the type of inheritance (public
vs protected
or private
). For instance consider:
struct B { virtual ~B() {} }; // polymorphic, so it can be used in a dynamic_cast
struct D1 : public B {}; // polymorphic by (public) inheritance
struct D2 : private B {}; // polymorphic by (private) inheritance
D1 d1;
D2 d2;
We have
static_assert(std::is_base_of<B, D1>::value, "");
static_assert(std::is_base_of<B, D2>::value, "");
assert(dynamic_cast<B*>(&d1));
assert(!dynamic_cast<B*>(&d2)); // Notice the negation.
Actually the last line yields a compiler error in GCC (error: 'B' is an inaccessible base of 'D2'
). VS2010 does compile it (yielding just a warning similar to GCC's error message).
(4) The requirements on the classes being polymorphic can be relaxed through an exception handling trick. Consider:
struct B { }; // not polymorphic
struct D1 : public B {}; // not polymorphic
struct D2 : private B {}; // not polymorphic
D1 d1;
D2 d2;
template <typename B, typename D>
const B* is_unambiguous_public_base_of(const D* obj) {
try {
throw obj;
}
catch (const B* pb) {
return pb;
}
catch (...) {
}
return nullptr;
}
Then we have
static_assert(std::is_base_of<B, D1>::value, "");
static_assert(std::is_base_of<B, D2>::value, "");
assert((is_unambiguous_public_base_of<B>(&d1)));
assert(!(is_unambiguous_public_base_of<B>(&d2))); // Notice the negation.
It's worth mentionning that is_unambiguous_public_base_of
is far slower than dynamic_cast
and (this became more obvious after the renaming mentioned in the update below) always returns a nullptr
for downcasts:
B* b1 = &d1;
assert(dynamic_cast<D1*>(b1)); // Requires D1 and B to be polymorphic.
assert(!(is_unambiguous_public_base_of<D1>(b1))); // Notice the negation.
A bit outdated reference on this trick is here.
Disclaimer: the implementation of is_unambiguous_public_base_of
above is just a draft to make the point and it doesn't handle const
and volatile
qualifications properly.
Update: In a previous version of this post is_unambiguous_public_base_of
was named my_dynamic_cast
and this was a source of confusion. So I renamed it to a more meaningful name. (Thanks to Jan Herrmann.)