的特化条件是否是逐渐收窄的:
, N, C>
, 0, C>
, 0, 1>
那么是否所有的情况都考虑到了呢?通过枚举出所有的特化条件,我们发现只有types<>没有考虑。对于types_erase来说,types<>没有删除的意义,因此直接让它匹配到types_erase的定义就可以了。当然,这会引起一个编译期的static_assert,因为任何的index都将超出types<>的范围。
五、types的查找,以及其它算法
查找算法的需求如下:
给定一个types和类型T,需要在types中找到T所在的第一个索引位置。
首先,我们先写出定义:
template
struct types_find : std::integral_constant
, check_is_types
{};
接着,我们用数学归纳法的方式来思考:
当types中的第一个元素为T时,索引位置为0;(终结条件)
当types中的第N个元素为T时,索引位置为上一个元素的索引加1。
那么我们可以先列出需要特化的版本:
, T1>
, U>
接下来,先特化终结条件:
template
struct types_find
, T1> : std::integral_constant
{};
然后思考一般情况:索引位置为上一个元素的索引加1,说明我们需要做一个加法。而find的结果有两种:找到了,和没找到。当没找到的时候,模板最终会匹配到types_find的定义上去。而我们在定义里给出的value是-1。因此在做加法运算时,需要把-1的情况忽略掉:
template
struct types_find
, U> : std::integral_constant
, U>::value == -1 ? -1 : types_find
, U>::value + 1)> {};
有了查找算法以后,判断types中是否存在某个类型就非常简单了:
template
struct types_exist
: std::integral_constant
::value != -1)> {};
接下来,让我们思考一个一般化的算法:
逐个遍历给定types中的元素,当该元素满足某个条件时,对这个元素做某件事情。
我们可以把定义写成下面这样:
template
class If_, typename V,
template
class Do_, typename U> struct types_do_if : check_is_types
{ using type = TypesT; };
If_用来把types中的某个类型T1,和给定的V做判断;Do_将接受If_的判断结果,对T1和U一起做某件事(比如置换)。
上面这句话说出来可能有点绕口,实际上写成代码并不复杂:
using done = typename Do_
::value, U, T1>::type;
我们从这里可以得到处理后的结果类型done。那么一般化的算法就是把done和剩下的(T1以外的)元素连起来。需要注意的是,处理是递归的,因此最后写出来应该是这个样子:
template
class If_, typename V,
template
class Do_, typename U> struct types_do_if
, If_, V, Do_, U> { private: using tail = typename types_do_if
, If_, V, Do_, U>::type; using done = typename Do_
::value, U, T1>::type; public: using type = typename types_link
::type; };
费这么大劲写这个一般化的算法有什么用呢?下面我们来看看它的威力。
首先,是types的置换算法:
给定一个types,以及类型T,U;要求把所有types中的T都换成U。
有了上面的types_do_if,实现这个算法非常轻松:
template
struct types_replace
: types_do_if
{};
当在types中找到类型T的时候,就把它变成U。代码和语言描述基本是一致的。
接下来,考虑一个移除的算法:
给定一个types,和类型T,要求从types中移除所有的T。
通过types_do_if实现如下:
template
struct types_remove
: types_do_if
> {};
我们可以看到,上面std::conditional后面的类型是types<>。原因是types_do_if里使用types_link连接结果。那么直接给定一个空的types,它和类型U连接后的结果仍然是U。
看到这里,我们其实可以写得更简单点:
template
struct types_remove
: types_replace
> {};
使用types<>置换掉types里的T,结果和移除是一样的。
这里再思考一步:如果需要移除的类型T本身,也是一个types列表,那么我们可以批量移除掉多个类型。实现算法其实很简单:
template
struct types_remove
> { private: using rm_t = typename types_remove
::type; public: using type = typename types_remove
>::type; };
从types
中取出一个元素做types_remove,把结果和剩下的types
放到递归里就可以了。
通过types_do_if还可以实现很多特殊操作,在这里就不再展开了。
接下来,我们实现types的“压缩”算法。当types里有多个重复元素的时候,如何把重复的内容剔除掉,只保留一个呢?
同样的,我们先写出定义:
template
struct types_compact : check_is_types
{ using type = TypesT; };
如何判断内容有重复?其实很简单,当我们从types中取出一个元素T1,那么剩下的内容里,所有的T1都将是重复的,删掉就可以了。
算法写出来就是这样:
template
struct types_compact
> { private: using rm_t = typename types_remove
, T1>::type; using tail = typename types_compact
::type; public: using type = typename types_link
::type; };
最后,一个特殊且有用的算法是倒序(reverse),即把types中的元素倒过来。实现如下:
template
struct types_reverse : check_is_types
{ using type = TypesT; }; template
struct types_reverse
> { private: using head = typename types_reverse
>::type; public: using type = type