When you want to figure out if A is a base of B, your compiler needs to know the type definitions or A and B must be the same (A is a base of A).
So you want to check if one template argument is derived from a forward declared type.
Judging from your later post, you also want to check that your CRTP base classes are actually used as base classes to one of the arguments.
In the latter case, the compiler does not have all the information when the CRTP base class is specialized, because the derived class has not been defined.
Thus, you have to defer the evaluation until a later stage, when the compiler has the necessary information. To do this, let's create a helper:
#include <utility>
class cl2;
class cl1;
template <typename Defer, typename Base, typename Derived>
struct deferred_is_base_of : std::is_base_of<Base, Derived>
{
};
In theory, this class could be specialized later on, so the compiler can not assume that defered_is_base_of
is the same as std::is_base_of
.
Assuming that we need a constructor (any other "required" function would also be OK):
template <typename Derived, typename Value>
class tl1
{
public:
template <typename Defer = void>
tl1()
{
static_assert(deferred_is_base_of<Defer, cl2, Value>::value,
"Value has to be derived from cl2");
static_assert(deferred_is_base_of<Defer, tl1, Derived>::value,
"Derived has to be derived from tl1");
}
};
The constructor is a template with a default parameter. Thus, you can call it like always. But the compiler has to wait for you to do it because you might use a different template parameter.
This does the trick. The compiler will evaluate the static assert only when the constructor is called. And by then, there will be enough information.
The second template works the same way:
template <typename Derived, typename Value>
class tl2
{
public:
template <typename Defer = void>
tl2()
{
static_assert(deferred_is_base_of<Defer, cl1, Value>::value,
"Value has to be derived from cl1");
static_assert(deferred_is_base_of<Defer, tl2, Derived>::value,
"Derived has to be derived from tl2");
}
};
class cl1 : tl1<cl1, cl2>
{
cl1()
{
}
};
class cl2 : tl2<cl2, cl1>
{
cl2()
{
}
};
int main() {}
Deferring template evaluation is a powerful tool in your TMP toolbox.
EDIT:
As @Brandon pointed out, the deferred base of is not required here. It is enough to put the check into the constructor to defer here.