为什么我们需要对template-template参数进行模板专门化

时间:2019-07-03 17:56:47

标签: c++ c++11 variadic-templates

以以下代码为例,确定类型列表的长度:

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

为什么我们需要标记的声明?我试图在几个编译器中编译没有此代码的代码,但总是会出错。

2 个答案:

答案 0 :(得分:1)

简短的答案是,没有主模板,我们将无法编写专门化的文字。

更长的答案是:如何从没有专门化的模板类型的实例中提取Types...?你不能。

这是尝试:

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

我们可以这样做:

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

但不是这样:

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

因为我们的模板需要一个template-template参数和一些类型,所以我们被迫只接受一个单一类型来匹配MyListType

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

但是现在我们面临另一个问题。我们如何分配value?我们需要某种方法来提取MyListType的模板参数,或者至少是数量。

我们需要一种匹配单个类型和其模板化参数的方法。因此,我们只需要匹配一个类型及其模板参数即可。

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);
};

第一个(不完整)类型是我们的主要模板。它使我们可以开始匹配单个类型,例如MyListType

第二种(完整)类型是我们的专长。它允许我们匹配单个类型,如果它是模板类型,则匹配用作模板参数的类型。

通过使第一种类型不完整,我们证明了仅允许专业化有效的意图。

答案 1 :(得分:1)

  

但是为什么我们首先需要专业化?

因为您按照以下方式使用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 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);
};