Let's consider the following CRTP code, with the slight modification that the derived class has to be a template one:
template<template<typename> class DERIVED, typename T>
struct crtp_base {};
template<typename T>
struct derived : crtp_base<derived,T> {};
I would like to define a type trait telling whether a class is derived from the CRTP base class. In other words, I need a type trait that works on a class template (and not on a type).
The first approach would be to define a generic version of the trait, with the peculiarity that the parameter is a template template one:
template<template<typename> class DERIVED>
struct is_crtp_based : std::false_type {};
Now, a first attempt for the template specialization of interest:
template<template<typename> class DERIVED, typename T>
struct is_crtp_based <crtp_base<DERIVED,T>> : std::true_type {};
but the compiler is not happy with this since it expects a class template but it finds a (non template) class crtp_base<DERIVED,T> instead.
Question: how should one write the specialization in such a case ? More generally, how to deal with type traits that take a class template as parameter ? I guess that there are already existing use cases like this but I don't find any of them right now.
Note: I can solve the question by the following trait that uses std::is_base_of :
template<template<typename> class DERIVED, typename T=void>
struct is_crtp_based : std::integral_constant<bool, std::is_base_of_v<crtp_base<DERIVED,T>,DERIVED<T> >> {};
static_assert (is_crtp_based<derived>::value == true);
but I would like to understand how to correct my first incorrect attempt through template specialization.
UPDATE: For a little bit more context, the crtp_base needs to know the T type, which explains the T template parameter in its definition and makes all the remaining more difficult (see my comment to 463035818_is_not_an_ai's answer).
I dare to claim that what you actually want is this:
To check whether a type derives from an instantiation of
crtp_baseyou can useAnd thats it.
No need for complicated template template arguments.
You want
Tto be available in the crtp base? Ok, write a trait for that:Now
get_T< derived<T>>::typeisT. In your base you can useget_T<DERIVED>. There is a template template argument, but only in one place, not all over your code.Note that the
get_Tis rather limited, it only works for templates with a single type argument. Though tahts exactly the same limitation as in your version ofcrtp_base.