typelist 学习

来源:互联网 发布:知乎欧美乐坛 编辑:程序博客网 时间:2024/05/29 14:03

下面我们先来看看如何定义一个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。不用多说了,这里类型萃取技术同样发挥了巨大的作用。



原创粉丝点击