Search code examples
c++templatesc++20c++-conceptspartial-specialization

Template function specialization for specific template ( not type )


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


Solution

  • 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';
    }