I have a class that works as wrapper for some primitives or custom types. I want to write explicit specialization for custom template type. My code that reproduces the problem:
template < class T >
struct A {
void func() { std::cout << "base\n"; }
};
template <>
struct A<int> {};
template < class T, class CRTP >
struct BaseCrtp {
void someFunc() {
CRTP::someStaticFunc();
}
};
struct DerrType : BaseCrtp<int, DerrType> {
static void someStaticFunc() {}
};
template < class T, class CRTP >
struct A< BaseCrtp<T, CRTP> > {
void func() { std::cout << "sometype\n"; }
};
int main() {
A<DerrType> a;
a.func(); // print: "base". should be: "sometype"
return 0;
}
A<DerrType>
use default function, not a specialization. How can I make specialization for these set of classes?
I will have a lot of types like DerrType
, and I want to make common behavior for all of them.
DerrType
and others will be used as curiously recurring template pattern
CodePudding user response:
Not sure I fully understood what you want, but maybe something like this:
template<typename T>
concept DerivedFromBaseCrtp = requires(T& t) {
[]<typename U, typename CRTP>(BaseCrtp<U, CRTP>&){}(t);
};
template < DerivedFromBaseCrtp T >
struct A<T> {
void func() { std::cout << "sometype\n"; }
};
The concept basically checks whether T
is equal to or is publicly inherited (directly or indirectly) from some specialization of BaseCrtp
. Otherwise the call to the lambda would be ill-formed. Template argument deduction only succeeds in the call if the argument and parameter type match exactly or the argument has a derived type of the parameter. If the class is inherited non-publicly, the reference in the call can't bind to the parameter.
The concept will however fail if the type is inherited from multiple BaseCrtp
specializations, in which case template argument deduction on the call will not be able to choose between the multiple choices.
Alternatively you can also use the stricter concept
template<typename T>
concept CrtpDerivedFromBaseCrtp = requires(T& t) {
[]<typename U>(BaseCrtp<U, T>&){}(t);
};
which will also require that the type is actually directly using the CRTP pattern on BaseCrtp
, not just that a base class is.