模板元编程之类型萃取(Type Traits)中匹配规则的推导方法

现我们希望在编译期判定

  1. 一个类型是否为常量类型
  2. 一个类型是否为引用类型
  3. 一个类型是否为模板类型

那么如何去做呢?

首先是写出泛匹配,泛匹配的结果一定为失败(false)。在本例中的三个需求都是针对单一类型的萃取,所以泛匹配只需要写一个参数即可:

template<typename> struct is_xxxx {
static constexpr bool result = false;
};

在这里,模板参数的参数名被省略,只需把xxxx改成对应的易读名即可,如is_constant_type、is_reference_type、is_template_class。所有的单一类型萃取都可以使用这个泛匹配,只需根据需要更改result的形式即可。比如在这里我们只需要判断是不是某种类型,所以我们使用bool,相应的特化匹配也只需要写一个即可。

接下来就要开始写特化匹配了。特化匹配一般比较难写,我们在这里分成四步:

  1. 写出最终要匹配的类型的一阶推导式
  2. 补齐对应的参数名和相关语法
  3. 写出对应的模板参数列表
  4. 完成特化匹配

一:写出最终要匹配的类型的一阶推导式

推导式的形式一般是:

元素 -> 语法描述

那么推导式的作用就是描述使用“语法描述”分解“元素”的方式,我们将这种方式成为文法。编译器会根据推导式描述的文法对程序进行解析,这种解析的过程称为匹配。

一阶推导式仅涉及到一次匹配,一般比较好写,但以后为了解决更复杂的问题我们可能要写高阶推导式甚至递归推导式,其中递归推导式涉及到消除无限递归的问题,我们以后会讲。

在这里,对应三个例子,有三个一阶推导式:

  1. ConstT -> const T
  2. RefT -> T& | T&&
  3. TemplateClass -> T<…>

注意,“…”指的是任意参数。后两个一阶推导式理论上是递归推导式,但我们不关心接下来的递归结果,所以也可以看作一阶推导。

问题:请列出下面几个类型符合的推导式以及推导结果

  1. const int&
  2. const std::map<std::string, std::size_t>

答案:

  1. 符合ConstT和RefT两个推导式,推导结果分别是T=int&、T=const int
  2. 符合ConstT和TemplateClass两个推导式,推导结果分别是T=std::map<std::string, std::size_t>、T=const std::map

二:补齐对应的参数名和相关语法

一般来说,需要补齐的有以下几个情况:

  1. 任意参数,需要用模板参数包表示出来
  2. 嵌套匹配,需要加上传递给嵌套匹配的参数

在这里需要补齐的只有第三个,补齐后的形式为:

TemplateClass  ->  T<X...>

三:写出对应的模板参数列表

这一步就比较简单,只需按照模板参数的语法规则对照写就可以了:

  1. template<typename T>
  2. template<typename T>
  3. template<template<typename…ArgsT>class T, typename…X>

其中,第三个模板参数列表还可以简化:

template<template<typename...>class T, typename...X>

为什么可以简化?因为这里ArgsT并没有实际意义,仅为了标记模板类参数的模板参数列表。

四:完成特化匹配

到这里我们只需要将我们完成的部分填入下面的模板:

模板参数列表 struct is_xxxx<推导式语法描述> {
static constexpr bool result = true;
};

在这里,既然是特化匹配,匹配结果必然是成功(true);除了填入完成的部分,还要把xxxx改成与泛匹配对应的易读名;像第二个判断类型是否为引用的特化匹配因为存在两个情况所以需要写两个特化匹配。

所以我们最终完成的代码如下:

// 一个类型是否为常量类型
template <typename T>
struct is_constant_type
{
static constexpr bool result = false;
};
template <typename T>
struct is_constant_type<const T>
{
static constexpr bool result = true;
};
// 一个类型是否为引用类型
template <typename T>
struct is_reference_type
{
static constexpr bool result = false;
};
template <typename T>
struct is_reference_type<T &>
{
static constexpr bool result = true;
};
template <typename T>
struct is_reference_type<T &&>
{
static constexpr bool result = true;
};
// 一个类型是否为模板类型
template <typename T>
struct is_template_class
{
static constexpr bool result = false;
};
template <template <typename... ArgsT> class T, typename... X>
struct is_template_class<T<X...>>
{
static constexpr bool result = true;
};

此代码符合C++11标准

发表回复