'Check if class is derived from a specific class (compile, runtime both answers available)

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?



Solution 1:[1]

You could use dynamic_cast.

if (dynamic_cast<DerivedClass*>(ptr)) {
    std::cout << "Class is derived from DerivedClass.";
}

Solution 2:[2]

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 3:[3]

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.

Solution 4:[4]

You can do this in several ways. The most common ones as the others have pointed out are dynamic_cast<> and std::is_base_of. The latter is used at compile time, while dynamic_cast<> can be used at runtime. HOWEVER, dynamic_cast<> will only work if your source class is polymorphic (i.e. has at least one virtual function - it can be a method or its destructor). If not, the compiler will trigger an error.

Solution 5:[5]

The compiler will only accept pointers to classes derived from your base class if your library functions takes pointers to the base class. My answer is with a classical approach type safety will handle it. To my experience that kind of type checking is enough. Having 25 years experience in the industry I question the need to do this check. Maybe such a fundamental question is not welcome? I would like to see the justification to having the need to do this kind of upcast. I never have to do that. The opposite, i.e. a down cast I need quite frequently.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 sashoalm
Solution 2
Solution 3 Community
Solution 4 Iosif Murariu
Solution 5