Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 752
Klaus Avatar asked May 07 '26 06:05

Klaus


2 Answers

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';
}
like image 77
StoryTeller - Unslander Monica Avatar answered May 09 '26 20:05

StoryTeller - Unslander Monica


One common workaround would go like this:

template <typename T> 
struct FuncImpl {
  static void Run() { std::cout << "Default"; }
};

template <typename T> 
struct FuncImpl<A<T>> {
  static void Run() { std::cout << "Specialized for A<T>"; }
};

template <typename T>
void Func() { FuncImpl<T>::Run(); }

Function templates cannot be partially specialized, but class templates can be; so we just delegate from the former to the latter.

like image 40
Igor Tandetnik Avatar answered May 09 '26 20:05

Igor Tandetnik