'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*>()));
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 |