'C++ check inheritance at compile time

I have a base class

template<typename T>
class Base {};

and a few derived classes:

class DerivedInt : public Base<int> {}
class DerivedDummy : public Base<Dummy> {} 
      // where Dummy is some user-defined concrete type
template<typename E>
class DerivedGeneric : public Base<E> {}

I wish to write a type trait function f<DerivedType>::value that returns true only when there exists a type T such that DerivedType inherits from Base<T>.

I feel that SFINAE is the way to go ... but I'm not too familiar with the metaprogramming black magic. Thanks!



Solution 1:[1]

An improvement to Mateusz Grzejek's solution to make a real traits:

template <class T>
std::true_type is_derived_from_base_t_impl(const Base<T>* impl);

std::false_type is_derived_from_base_t_impl(...);

template <class Derived>
using is_derived_from_base_t =
    decltype(is_derived_from_base_t_impl(std::declval<Derived*>()));

Live demo

Note that doesn't handle multiple inheritance.

Solution 2:[2]

I believe the type support templates in C++11 are what you want, especially std::is_base_of.

Solution 3:[3]

I am not sure, if std::is_base_of could be helpful in this case, but you could surely use std::is_convertible.

If there exists type T, such that Derived inherits from Base<T>, it means, that Derived is implicitly-convertible to Base<T>.

So, you can simply use following solution. It is compile-time detection in such way, that it won't compile if you call checking function for type, that does not fulfil your requirements. Check this code:

#include <iostream>


struct Dummy
{
};

template<typename T>
class Base
{
};

class DerivedInt : public Base<int>
{
};

class DerivedDummy : public Base<Dummy>
{
};

template<typename E>
class DerivedGeneric : public Base<E>
{
};

template <class T>
bool is_derived_from_base_t_impl(Base<T>* impl)
{
    return true;
}

template <class Derived>
bool is_derived_from_base_t()
{
    Derived* d = nullptr;
    return is_derived_from_base_t_impl(d);
}


int main()
{
    std::cout<< is_derived_from_base_t< DerivedInt >() <<"\n";
    std::cout<< is_derived_from_base_t< DerivedDummy >() <<"\n";
    std::cout<< is_derived_from_base_t< DerivedGeneric<float> >() <<"\n";

    return 0;
}

Output:

1
1
1

However, if you do:

is_derived_from_base_t< float >();

You will get:

error C2784: 'bool is_derived_from_base_t_impl(Base<T> *)' : could not deduce template argument for 'Base<T> *' from 'float *'

(Output from VC++ 11)

Solution 4:[4]

Several more aspects answered

* independence from C++11 features
* private inheritance
* virtual inheritance
* multiple inheritance
* inheritance from struct
* symmetric relationship, i.e. forward or reverse inheritance

/**
 * Class Template InheritanceCheck
 * @param Candidate - type optionally inherited from Base
 * @param Base - base class/struct
 */
template<typename Candidate, typename Base>
class InheritanceCheck
{

    static char Test(Base*);
    static int Test(...);

    static void Constraints(Candidate* p) { }

    InheritanceCheck() 
    {
        void(*p)(Candidate*) = Constraints;
    };

public:
    /**
     *  isSub, true when Candidate is derived from Base
     */
    static const bool isSub = sizeof(Test(static_cast<Candidate*>(0))) == sizeof(char);

    /**
     *  isRelated, true when Candidate is derived from Base OR Base is derived from Candidate
     */
    static const bool isRelated = isSub || InheritanceCheck<Base,Candidate>::isSub;
};


// Access policy for class InheritanceCheck in case of protected/private inheritance
#define ICP template<typename Candidate, typename Base> friend class InheritanceCheck

/* Example */
class A {};
class B : virtual A { ICP; };
class C : public virtual A, public B {};
struct D {};
struct S {};
class Z : public S {};

int main()
{
    std::cout << "B->A " << InheritanceCheck<B, A>::isSub << std::endl;
    std::cout << "C->A " << InheritanceCheck<C, A>::isSub << std::endl;
    std::cout << "C->B " << InheritanceCheck<C, B>::isSub << std::endl;
    std::cout << "D->A " << InheritanceCheck<D, A>::isSub << std::endl;
    std::cout << "A->B " << InheritanceCheck<A, B>::isSub << std::endl;
    std::cout << "A~B " << InheritanceCheck<A, B>::isRelated << std::endl;
    std::cout << "S->A " << InheritanceCheck<S, A>::isSub << std::endl;
    std::cout << "Z->S " << InheritanceCheck<Z, S>::isSub << std::endl;
}

Output:
B->A 1
C->A 1
C->B 1
D->A 0
A->B 0
A~B 1
S->A 0
Z->S 1

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 Jarod42
Solution 2 AndiDog
Solution 3 Mateusz Grzejek
Solution 4 Sam Ginrich