I have some templated class types like A,B,C as follows:
template < typename T >
class A{};
template < typename T >
class B{};
template < typename T >
class C{};
And now I want to have a function which accepts in general any type like:
template < typename T>
void Func()
{
std::cout << "Default " << __PRETTY_FUNCTION__ << std::endl;
}
And now I want to specialize the function to only accept one of the given template classes like:
template < typename T>
void Func<A<T>>()
{
std::cout << "All A Types " << __PRETTY_FUNCTION__ << std::endl;
}
Which is not allowed because it is only a partial specialization. OK.
I think concept
may help but it feels that I think much too complicated. My solution is:
template < typename T, template <typename > typename OUTER >
bool Check;
template < typename INNER, template < typename > typename OUTER, template < typename> typename T>
constexpr bool Check< T<INNER>, OUTER > = std::is_same_v< OUTER<INNER>, T<INNER>>;
template < typename T >
concept IsA = Check< T, A >;
template < typename T >
concept IsB = Check< T, B >;
template < IsA T >
void Func()
{
std::cout << "All A Types " << __PRETTY_FUNCTION__ << std::endl;
}
template < IsB T >
void Func()
{
std::cout << "All B Types " << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
Func<A<int>>();
Func<B<int>>();
Func<C<int>>();
}
It feels a bit complicated to me. Can that be simplified? Would be nice if the Check
template can be removed. Any idea?
See full example here live
It feels a bit complicated to me. Can that be simplified? Would be nice if the Check template can be removed. Any idea?
Much of complexity and in-elegance is in the fact you need a new concept for every class template. Write a general-purpose and reusable concept, and it is no longer complicated to use.
template <typename T, template <typename...> class TT>
constexpr bool is_instantiation_of_v = false;
template <template <typename...> class TT, typename... TS>
constexpr bool is_instantiation_of_v <TT<TS...>, TT> = true;
template <class C, template<typename...> class TT>
concept instantiation_of = is_instantiation_of_v<C, TT>;
The same principle as yours, except the checker is usable with a template taking any number of type arguments. Meanwhile, the concept accepts the same parameters. The first parameter has a special meaning and is implicitly understood to be the constrained template parameter in the short-hand syntax. The rest (the template template-parameter) must be given explicitly.
How can it be used? Like this
template <instantiation_of<A> T>
int Func()
{
return 'A';
}
template <instantiation_of<B> T>
int Func()
{
return 'B';
}
Got a new class template to constrain over? No problem, this concept works without additional boiler-plate.
template <instantiation_of<D> T>
int Func()
{
return 'D';
}