一、typelisttypelisttypelist的介绍
typelisttypelisttypelist这个概念比较系统的出现是在Modern C++ DesignModern \ C++ \ DesignModern C++ Design一书中,关于typelisttypelisttypelist的解释,就是用来操作一大堆类型的C++C++C++容器,就像std::list
一样能够为数值提供各自操作一样,不过这里操作的是类型,而不是数值。
这里,基于C++11C++11C++11以上的标准实现typelisttypelisttypelist的部分接口。
二、typelisttypelisttypelist的实现
这里我们将所有的操作都放入tpltpltpl这个命名空间,防止命名污染。
2.1 typelist
的定义
首先,typelisttypelisttypelist的定义十分简单,如下,就是一个可变参变量类模板:
template<typename ... T>
class typelist {
};
为了方便操作,我们使用类型别名来创建两个typelist
,如下:
using TPL_NUM1 = tplt::typelist<int, double, float, char, bool>;
using TPL_NUM2 = tplt::typelist<>;
2.2 查看typelist
的第一个元素
就是在特化版本上,将可变参分离,萃取出第一个元素即可:
//获取typelist的第一个元素
//泛化版本
template<typename TPLT>
class front;
//特化版本
template<typename FirstElem, typename ... OtherElem>
class front<typelist<FirstElem, OtherElem...>> {
public:
using type = FirstElem;
};
void Test1() {
std::cout << typeid(TPL_NUM1).name() << "第一个元素类型为:" << typeid(tplt::front<TPL_NUM1>::type).name() << std::endl;
//如果typelist为空,则会编译失败
//std::cout << typeid(TPL_NUM2).name() << "第一个元素类型为:" << typeid(tplt::front<TPL_NUM2>::type).name() << std::endl;
}
如果为空的typelisttypelisttypelist,则会编译失败,运行结果如下:
2.3 获取typelist
的元素个数
这个接口直接使用sizeof ...()
操作符即可,设置valuevaluevalue为static inline constexpr
修饰:
//获取typelist的元素个数
//特化
template<typename TPLT>
class size;
//泛化
template<typename ...Args>
class size<typelist<Args...>> {
public:
static inline constexpr size_t value = sizeof...(Args);
};
void Test2() {
std::cout << "typelist1的大小为:" << tplt::size<TPL_NUM1>::value << std::endl;
std::cout << "typelist2的大小为:" << tplt::size<TPL_NUM2>::value << std::endl;
}
运行结果如下:
2.4 移除typelist
第一个元素
同样也是利用特化版本,分离第一个参数和剩下的参数,用剩下参数构造出一个typelist
即可:
//移除第一个元素
//泛化版本
template<typename TPLT>
class pop_front;
//特化版本
template<typename FirstElem,typename... OtherElem>
class pop_front < typelist < FirstElem, OtherElem... >> {
public:
using type = typelist<OtherElem...>;
};
void Test3() {
std::cout << "移除第一个元素后类型为:" << typeid(tplt::pop_front<TPL_NUM1>::type).name() << std::endl;
//如果没有元素,则会编译失败
//std::cout << "移除第一个元素后类型为:" << typeid(tplt::pop_front<TPL_NUM2>::type).name() << std::endl;
}
运行结果如下:
2.5 向typelist
的开头和结尾插入一个新的元素
push_front
和push_back
的接口实现类似,只是在分离参数的时候,将新的参数置于第一个和第二个:
//向开头插入元素
//泛化版本
template<typename TPLT, typename NewElem>
class push_front;
//特化版本
template<typename... Elems,typename NewElem>
class push_front<typelist<Elems...>, NewElem> {
public:
using type = typelist<NewElem, Elems...>;
};
//向结尾插入元素
//泛化版本
template<typename TPLT,typename NewElem>
class push_back;
//特化版本
template<typename... Elems, typename NewElem>
class push_back <typelist <Elems...>, NewElem> {
public:
using type = typelist < Elems..., NewElem>;
};
void Test4() {
std::cout << "typelist1向前插入第一个元素后类型为:" << typeid(tplt::push_front<TPL_NUM1,__int64>::type).name() << std::endl;
std::cout << "typelist2向前插入第一个元素后类型为:" << typeid(tplt::push_back<TPL_NUM2, __int64>::type).name() << std::endl;
std::cout << "typelist1向后插入第一个元素后类型为:" << typeid(tplt::push_back<TPL_NUM1, __int64>::type).name() << std::endl;
std::cout << "typelist2向后插入第一个元素后类型为:" << typeid(tplt::push_back<TPL_NUM2, __int64>::type).name() << std::endl;
}
运行结果如下:
2.6 替换typelist
的第一个元素
在特化版本分离可变参的时候,将第一个参数替换为新的参数即可:
//替换开头元素
//泛化版本
template<typename TPLT,typename NewElem>
class replace_front;
//特化版本
template<typename FirstElem,typename... OtherElems,typename NewElem >
class replace_front<typelist<FirstElem, OtherElems...>, NewElem> {
public:
using type = typelist<NewElem, OtherElems...>;
};
void Test5() {
std::cout << "typelist1替换第一个元素后类型为:" << typeid(tplt::replace_front<TPL_NUM1, __int64>::type).name() << std::endl;
//如果没有元素,则会编译失败
//std::cout << "typelist2替换第一个元素后类型为:" << typeid(tplt::replace_back<TPL_NUM2, __int64>::type).name() << std::endl;
}
结果如下:
2.7 判断typelist
是否为空
可以直接继承std::true_typ
和std::false_type
或者自己内部实现一个valuevaluevalue,这里采用前者。
对空的typelisttypelisttypelist实现一个特化,继承std::true_type
即可。而泛化版本直接继承std::false_type
代码如下:
//判断是否为空
//泛化版本
template<typename TPLT>
class is_empty :public std::false_type {
};
//特化版本
template<>
class is_empty<typelist<>> :public std::true_type {
};
void Test6() {
std::cout << "typelist1是否为空:" << tplt::is_empty<TPL_NUM1>::value << std::endl;
std::cout << "typelist2是否为空:" << tplt::is_empty<TPL_NUM2>::value << std::endl;
}
运行结果如下:
2.8 随机访问typelist
的元素
我们直接继承之前写的front
类模板,然后每次就能取出第一个元素,使用递归的方式了,传入一个indexindexindex,每次index−1index-1index−1。
设置一个特化版本,index=0index = 0index=0,此时去除的第一个元素就是索引位置的元素了:
//根据索引号查找某个元素
//泛化版本
template<typename TPLT,size_t index_v>
class find :public find<typename pop_front<TPLT>::type, index_v - 1> {
};
//特化版本
template<typename TPLT>
class find<TPLT, 0> :public front<TPLT> {
};
void Test7() {
std::cout << "typelist1[0] = " << typeid(tplt::find<TPL_NUM1, 0>::type).name() << std::endl;
std::cout << "typelist1[1] = " << typeid(tplt::find<TPL_NUM1, 1>::type).name() << std::endl;
std::cout << "typelist1[2] = " << typeid(tplt::find<TPL_NUM1, 2>::type).name() << std::endl;
std::cout << "typelist1[3] = " << typeid(tplt::find<TPL_NUM1, 3>::type).name() << std::endl;
//如果不存在,则会编译失败
#if 0
std::cout << "typelist1[4] = " << typeid(tplt::find<TPL_NUM1, 4::type).name() << std::endl;
std::cout << "typelist2[0] = " << typeid(tplt::find<TPL_NUM2, 0>::type).name() << std::endl;
#endif
}
结果如下:
2.9 获取typelist
中大小最大的类型
这个接口的实现就比较复杂了,需要使用到std::conditional
条件分支,使用sizeof
比大小。
在类内部,还需要分离第一个元素和剩余的元素,在剩余的元素还需要分离出第一个元素进行比大小,这样递归下去。
最后实现一个空的typelist
特化版本,作为递归的出口。特化版本的类型为charcharchar类型,因为charcharchar类型占一字节,为大小最小的类型。
整体代码如下:
//获取大小最大的类型
//泛化版本
template<typename TPLT>
class get_maxsize_type {
private:
using tl_first_elem = typename front<TPLT>::type;
using tl_remain = typename pop_front<TPLT>::type;
//递归下去的第一个元素
using tl_first_elem_rec = typename get_maxsize_type<tl_remain>::type;
public:
using type = typename typename std::conditional_t<
sizeof(tl_first_elem) >= sizeof(tl_first_elem_rec), tl_first_elem, tl_first_elem_rec>;
};
//特化版本
template<>
class get_maxsize_type <typelist<>> {
public:
using type = char; //一个字节,大小最小的类型
};
2.10 反转typelist
的容器序列
反转序列实际上可以看作是,取出第一个元素,插入到后面,不断递归下去,最终得到的typelist
就是反转的typelist
了。
需要有两个特化版本,其中一个为typelist
为空的时候,此时返回空的typelist
即可,作为递归的出口。
另一个特化版本是当typelist
不为空的时候,此时需要促使递归的进行,不断取出第一个元素,并放入最后
所以还需要设置boolboolbool 参数为is_empty
类模板的valuevaluevalue成员,判断是否为空:
//反转typelist的元素顺序
//泛化版本
template<typename TPLT,bool = is_empty<TPLT>::value>
class reverse;
//特化版本1,当typelist存在元素时
template<typename TPLT>
class reverse <TPLT, false> {
private:
using tl_first_elem = typename front<TPLT>::type;
//促使递归的using
using tl_result_elem_rec = typename reverse<typename pop_front<TPLT>::type>::type;
public:
using type = typename push_back<tl_result_elem_rec, tl_first_elem>::type;
};
//特化版本2,用于typelist为空
template<typename TPLT>
class reverse<TPLT, true> {
public:
using type = typename TPLT;
};
void Test9() {
std::cout << "typelist1反转前类型为:" << typeid(TPL_NUM1).name() << std::endl;
std::cout << "typelist1反转后类型为:" << typeid(tplt::reverse<TPL_NUM1>::type).name() << std::endl;
//空元素反转前后不变
std::cout << "typelist2反转前类型为:" << typeid(TPL_NUM2).name() << std::endl;
std::cout << "typelist2反转后类型为:" << typeid(tplt::reverse<TPL_NUM2>::type).name() << std::endl;
}
运行结果如下: