下面我们先来看看如何定义一个TypeList,这样可以对于TypeList先有一个感性的认识。
typedef TYPELIST_3(char, int, long) MyTypeList;
定义了一个具有三个元素的TypeLsit,这三个元素分别为:char、int、long。TYPELIST_3为Loki库中提供的用于定义TypeList的工具,我们会在本文的后面进行介绍。
::Loki::Length<MyTypeList>::value;
计算MyTypeList中元素的个数,结果为3。
typedef ::Loki::TypeAt<MyTypeList, 1>::Result MyType;
获取MyTypeList中第1个元素(从0开始),此时MyType就是int。
typedef ::Loki::Append<MyTypeList, float>::Result MyTypeList1;
向MyTypeList中在添加一个元素:float,结果为MyTypeList1。此时::Length<MyTypeList1>::value等于4。
TypeList定义
为了能够一致的进行TypeList的操作,在Loki库中定义了一个空类型NullType来标记一个TypeList的结束,NullType和TypeList的定义如下:
class NullType { };
template <class T, class U>
struct Typelist
{
typedef T Head;
typedef U Tail;
};
对于规范型TypeList的定义采用了一种尾递归的方法:
NullType是规范的TypeLsit
如果T是规范的TypeList,那么对于任意原子类型U,TypeList<U,T>是规范的TypeList
Loki库中所采用的TypeList均为规范型的TypeList,这样可以在不减少灵活性的前提下简化对于TypeList的操作。本文后面所指的TypeList均为规范型的。
如何定义一个TypeList呢?比如:包含:char、int、long三个元素的TypeLsit。根据上面的定义,可以得到如下的定义形式:
TypeList<char, TypeLsit<int, TypeLsit<long, NullType> > >; // 注意两个>间一定要加一个空格,不
// 然编译器会认为是 “>>” 操作符
这样的定义方法显得比较麻烦、罗嗦,为了简化用户对于TypeList的使用,Loki库中采用了宏定义的方式对于大小在1~50的TypeList进行了预定义:
#define TYPELIST_1(T1) ::Loki::Typelist<T1, ::Loki::NullType>
#define TYPELIST_2(T1, T2) ::Loki::Typelist<T1, TYPELIST_1(T2) >
#define TYPELIST_3(T1, T2, T3) ::Loki::Typelist<T1, TYPELIST_2(T2, T3) >
…
依此类推。
这样用户在使用起来就会比较方便一些。
TypeList典型操作实现
了解了TypeList的定义,这里我们将对于TypeList相关的三个典型操作(Length、TypeAt和Append)的实现进行详细的剖析。掌握了这几个典型的操作,再学习其他的操作就会变得非常的容易。
我们将通过一个实例进行讲解,来看一下编译器实际的运作过程。我们定义了一个包含两个元素:int以及long的TypeList。
typedef TYPELIST_2(int, long) MyTypeList;
此时,编译器会根据TypeList的定义方式产生如下的类型定义结果:
struct TypeList<long, NullType>
{
typedef long H;
typedef NullType T;
};
struct TypeList<int , TypeList<long, NullType > >
{
typedef int H;
typedef TypeList<long, NullType> T;
};
Length的实现 - 获取TypeList中的元素个数:
template <class TList> struct Length; // 仅有声明,没有实现,如果所传入的类型不是TypeLsit
// 的话,会产生一个编译期错误
template <> struct Length<NullType> // 递归调用的结束条件,NullType的大小为0,运用了
{ // 模板特化和类型萃取技
// 术
enum { value = 0 };
};
template <class T, class U> // 递归的规则定义,运用了模板偏特化和类型萃取技术
struct Length< Typelist<T, U> >
{
enum { value = 1 + Length<U>::value };
};
当通过Length<MyTypeList>::value获得MyTypeList中的元素个数时,看看编译器是如何根据我们指定的规则进行递归调用的。首先编译器会生成如下几个版本的Length定以:
struct Length<TypeList<long, NullType> >
{
enum { value = 1 + Length<NullType>::value };
};
struct Length<TypeList<int, TypeList<long, NullType> > >
{
enum { value = 1 + Length<TypeList<long, NullType> >::value };
};
根据Length结束条件的定义可知,Length<NullType>::value等于0,所以Length<TypeList<long, NullType> >::value就等于Length<NullType>::value+1,也就是1。通过递推可知,Length<MyTypeList>::value也就是Length<TypeList<int, TypeList<long, NullType> > >::value等于Length<TypeList<long, NullType> >::value+1,也就是2。在层层的递推过程中,类型萃取技术得到了充分的体现,value就是我们想要得到的TypeList类型相关的信息,在每一层的递归过程中,都是通过它来保留结果的。
TypeAT的实现 - 获取给定位置处的元素
template <class TList, unsigned int index> struct TypeAt; // 仅有声明,没有实现,如果所传入
// 的类型不是TypeLsit
// 的话,会产生一个编译期错误
template <class Head, class Tail>
struct TypeAt<Typelist<Head, Tail>, 0> // 递归调用的结束条件,如果给定位置为0,则
{ // 返回TypeList中的第一个元素
typedef Head Result;
};
template <class Head, class Tail, unsigned int i> //递归规则定义,注意这里的返回结果为类型,
struct TypeAt<Typelist<Head, Tail>, i> // 运用了类型萃取技术。typename关键字的作
{ // 用是告诉编译器其后的实体是类型。
typedef typename TypeAt<Tail, i - 1>::Result Result;
};
下面看看使用TypeAt<MyTypeList, 1>::Result时,编译器都产生了那些动作。首先编译器要根据递归规则生成如下的类型定义:
struct TypeAt<TypeList<long, NullType> , 0>
{
typedef long Result;
};
struct TypeAt<TypeList<int , TypeList<long, NullType> > , 1>
{
typedef TypeAt<TypeList<long, NullType> , 0>::Result Result;
};
很明显typedef TypeAt<TypeList<long, NullType> , 0>::Result Result; 就是typedef long Result;所以,TypeAt<MyTypeList, 1>::Result就是long类型。同样的,在这个实现中充分使用了类型萃取技术,不过,这里我们想要的不是value,而是Result。
Append的实现 - 在TypeList的末尾添加一个元素
template <class TList, class T> struct Append; // 仅有声明,没有实现,如果所传入
// 的类型不是TypeLsit
// 的话,会产生一个编译期错误
template <> struct Append<NullType, NullType> // 递归结束条件定义,模板的偏特化
{
typedef NullType Result;
};
template <class T> struct Append<NullType, T> //递归结束条件定义,模板的偏特化
{
typedef TYPELIST_1(T) Result;
};
template <class Head, class Tail>
struct Append<NullType, Typelist<Head, Tail> > // 递归结束条件定义,模板的偏特化
{
typedef Typelist<Head, Tail> Result;
};
template <class Head, class Tail, class T> // 递归规则定义,注意这里的返回结果为类型,
struct Append<Typelist<Head, Tail>, T> // 运用了类型萃取技术。typename关键字的作
{ // 用是告诉编译器其后的实体是类型。模板的偏特
// 化
typedef Typelist<Head,
typename Append<Tail, T>::Result>
Result;
};
同样的,让我们来看看当使用Append<MyTypeList, float>::Result时,编译器的递归执行动作。首先看看编译器会生成的一些类型定义:
struct Append<NullType, float>
{
typedef TYPELIST_1(float) Result;
};
struct Append<TypeList<long, NullType> , float>
{
typedef TypeList<long, Append<NullType, float>::Result > Result;
};
struct Append<TypeList<int, TypeList<long, NullType> >, float>
{
typedef TypeList<int, Append<TypeList<long, NullType>,float >::Result > Result;
};
经过简单的替换,Append<MyTypeList, float>::Result就等于:
typedef TypeList<int, Append<TypeList<long, NullType>,float >::Result >;
等于:
typedef TypeList<int, TypeList<long, Append<NullType, float>::Result > >;
等于:
typedef TypeList<int, TypeList<long, TypeList<float, NullType> > >;
也就是:
TYPELIST_3(int, long, float) ;等同于在原有TypeList的末尾添加了一个新元素float。不用多说了,这里类型萃取技术同样发挥了巨大的作用。