Но зачем вообще нужна специализация?
Поскольку вы используете class
следующим образом
std::cout << type_list_length<type_list<int, long, long long>>::value;
// ...........................^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- template argument
или также
std::cout << type_list_length<std::tuple<int, long, long long>>::value;
// ...........................^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <- template argumen
или аналогичным образом.
Обратите внимание на аргумент шаблона: в обоих случаях тип ; type_list<int, long, long long>
в первом случае std::tuple<int, long, long long>
.
То есть вы не можете объявить type_list_length
как получающий тип шаблона-шаблона
template <template <class...> class type_list, class... Types>
struct type_list_length // <--- doesn't work
{
static constexpr std::size_t value = sizeof...(Types);
};
потому что вы должны вызывать его, передавая параметр template-template, а затем переменный список шаблонов; Я имею в виду ... вы должны использовать его следующим образом
std::cout << type_list_length<type_list, int, long, long long>::value;
std::cout << type_list_length<std::tuple, int, long, long long>::value;
но, таким образом, вы теряете силу класса: извлекаете и считаете параметр шаблона параметра типа.
Итак, сначала нужно объявить type_list_length
как получающий тип
template <typename> // no template parameter name needed here (not used)
struct type_list_length;
, а затем объявить и определить специализацию в случае, если полученный тип является шаблоном-шаблоном с аргументами
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);
};