Check if class is derived from a specific class (compile, runtime both answers available)
Solution 1
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 available in the following links:
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.)
Solution 2
You could use dynamic_cast.
if (dynamic_cast<DerivedClass*>(ptr)) {
std::cout << "Class is derived from DerivedClass.";
}
Solution 3
Check if class is derived from a specific class (compile time)
You can use std::is_base_of
:
#include <type_traits>
....
const bool isBase = std::is_base_of<base, TheOtherClass>::value;
isBase
is true if TheOtherClass
is derived from base
.
Solution 4
I think the answer to this question is very difficult. Of course there is std::is_base_of
and dynamic_cast
. Both provide you with some very limited information. The third option is function overloading. With all of those techniques you can choose a special path in your code which should be executed.
std::is_base_of
can be interpreted in a boolean context and it is derived from either std::true_type
or std::false_type
. This fact makes it possible to use it as paramter for a function and use compile time polymorphism via function overloading. This first example shows how to use it in a boolean context, but you don't have any further specific type information. So compilation will fail in most circumstances (see here for a further description):
template<class T>
void do_it1(T const& t) {
if (std::is_base_of<T,derived1>::value) {
// we have a derived1
} else {
// we have a base
}
}
The second version is simple function overloading. Here compile time polymorphism is used but all runtime type information is lost (except virtual functions and dynamic_cast
are used):
void do_it2(Base const& b) {
// we have a base your algorithm for base here
}
void do_it2(Derived2 const& d) {
// Derived algorithm here
}
Now the third version combines both:
template<class T>
void do_it3_impl(T const& t, std::true_type) {
// here t will be of a type derived from derived1
}
template<class T>
void do_it3_impl(T const& t,std::false_type) {
// here t will be of type not derived from derived1
}
template<class T>
void do_it_3(T const& t) {
do_it3_impl(t, std::is_base_of<T,derived1>()); // here we forward to our impl
}
The third variant is normaly used for header only libraries which don't use runtime poylmorphism (search for std::advance
for an excample).
Now to runtime polymorphism. Here you have the dynaminc_cast
variant:
void do_it4(Base const* ptr)
if (derived1 const* obj = dynamic_cast<derived*>(ptr)) {
// here we have obj with type derived1*
} else {
// here we have only base
}
If this variant is not fast enough you can implement your onw cast to derived1:
class derived1;
class base {
// as above
public:
virtual derived1 const* to_derived1() const {
return 0;
}
};
class derived1
: public base
{
// ...
virtual derived1 const* to_derived1() const {
return this;
}
};
void do_it5(Base const* ptr)
if (derived1 const* obj = ptr->to_derived1() {
// here we have obj with type derived1*
} else {
// here we have only base
}
This is fast but it scales very well for only very few (approximately 1) derived classes.
Last but not least you should think about your class design and deside what methods to make virtual and implement in base
, derived1
or other classes. You should definitly look for strategy pattern.
khajvah
A bored web developer. Currently interested in: OS development Mathematics (though I don't have much knowledge in this subject) (maybe) Statistics What I hate: Corruption Email: [email protected]
Updated on February 05, 2022Comments
-
khajvah over 2 years
It is easier to explain on an example so,
class base { //.... } class derived1 : public base { //... }
In my library, there is a pointer of base class. The user of the library have to make classes derived from either base or derived1 and assign pointer to that class.
How can I check what class is user-defined class derived from?