sigy
sigy

Reputation: 2500

Why do we need a template specialization for a template-template parameter

Take as an example this code to determine the length of a type list:

template <class... Types>
class type_list {};

template <class TypeList>
struct type_list_length;  // <---

template <template <class...> class type_list, class... Types>
struct type_list_length<TypeList<Types...>>
{
    static constexpr std::size_t value = sizeof...(Types);
};

Godbolt

Why do we need the marked declaration? I tried to compile the code without it in several compilers but are always getting errors.

Upvotes: 1

Views: 66

Answers (2)

max66
max66

Reputation: 66230

But why do we need the specialization in the first place?

Because you use the class as follows

std::cout << type_list_length<type_list<int, long, long long>>::value;
// ...........................^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- template argument

or also

std::cout << type_list_length<std::tuple<int, long, long long>>::value;
// ...........................^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- template argumen

or in a similar way.

Observe the template argument: in both cases is a type; type_list<int, long, long long> in first case, std::tuple<int, long, long long>.

So you can't declare type_list_length as receiving a template-template type

template <template <class...> class type_list, class... Types>
struct type_list_length // <--- doesn't work
{
    static constexpr std::size_t value = sizeof...(Types);
};

because you should call it passing a template-template parameter followed by a variadic list of templates; I mean... you should use it as follows

std::cout << type_list_length<type_list, int, long, long long>::value;
std::cout << type_list_length<std::tuple, int, long, long long>::value;

but, this way, you loose the power of the class: extract and count the template parameter of the type parameter.

So you need first declare type_list_length as receiving a type

template <typename> // no template parameter name needed here (not used)
struct type_list_length; 

and then declare and define a specialization in case the received type is a template-template with arguments

template <template <typename...> class type_list, typename... Types>
struct type_list_length<TypeList<Types...>>
{ // ...................^^^^^^^^^^^^^^^^^^   the parameter is a type
    static constexpr std::size_t value = sizeof...(Types);
};

Upvotes: 1

AndyG
AndyG

Reputation: 41110

The short answer is that without the primary template we cannot write a specialization.

The longer answer is: how would you extract Types... from the instantiation of a templated type without a specialization? You cannot.

Here's an attempt:

template <template <class...> class type_list, class... Types>
struct type_list_length
{
    static constexpr std::size_t value = sizeof...(Types);
};

We can do this:

type_list_length<type_list, int, double, float>::value

But not this:

using MyListType = type_list<int, double, float>;
type_list_length<MyListType>::value;

Because our template expects a template-template parameter and some types, so we're forced to accept just a single type to match MyListType:

template <class T>
struct type_list_length
{
    static constexpr std::size_t value = // ????;
};

But now we're faced with another issue. How do we assign value? We need some way to extract the template arguments for MyListType, or at least the count.

We need a way to match a single type and the arguments it is templated on. Hence, we need match just a single type AND its template parameters.

template <class TypeList>
struct type_list_length; 

template <template <class...> class type_list, class... Types>
struct type_list_length<TypeList<Types...>>
{
    static constexpr std::size_t value = sizeof...(Types);
};

The first (incomplete) type is our primary template. It allows us to start matching a single type, like MyListType.

The second (complete) type is our specialization. it allows us to match a single type AND if it's a templated type, match the types used as template parameters for it.

By leaving the first type incomplete, we demonstrate our intention to ONLY allow the specialization to be valid.

Upvotes: 1

Related Questions