踏入新的世界,属于我自己的类型容器:TypeVector

本文介绍了TypeVector的设计理念,对比TypeList的不足之处,详细阐述了TypeVector的算法实现,包括求取长度、获取指定索引类型、添加及删除类型等操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TypeList固然精妙,但是就像链表相对数组有其不足一样,TypeList中的类型递归定义使得取得其中某个类型会变成比较麻烦的事,从而也把算法的复杂性提高了,或许这些都还能忍受,比较Loki::TypeList提供了足够的算法供我们使用,但是TypeList的一个致命弱点却在我编写通讯兵模式时候发现了。

 

4.1 TypeList的不足

当我的程序需要记录某个函数的参数类型时候我们用到TypeList很方便,

例如下面的程序:

       template<typename TList>

class DoSomeThing

{

        typedef TypeAt<TList,0>::Result Parm0;

        typedef TypeAt<TList,1>::Result Parm1;

        typedef TypeAt<TList,2>::Result Parm2;

public:

        void Do(){};

        void Do(Parm0 p0){};

        void Do(Parm0 p0,Parm p1){};

        void Do(Parm0 p0,Parm p1,Parm p2){};

};

    我们可以用不同类型特化DoSomeThing以后根据参数个数不同调用成员函数Do的重载体,这个技法在我后面的程序中将会出现很多,这就可以让我们在特化DoSomeThing之前不用知道具体会有几个参数或者会是什么类型,这就达到了泛化的目的。

    但是问题出来了,如果我们需要Do的重载体接收的参数类型不是一个单数据类型,而是需要接收一个TypeList容器的时候,TypeList似乎就开始无能为力了,因为我们虽然可以写出类型这样的代码

typedef TYPELIST_2(int,unsigned int) IntType;

typedef TYPELIST_2(char,unsigned char) CharType;

typedef TYPELIST_2(IntType,CharType) SomeType;

但是我们却不能用TypeAt提取出SomeType中的IntType,如果我们用下面的代码得到的结果将不是我预期的:

TypeAt<SomeType,0>::Result

我们也许希望得到一个IntType,但是程序会返回一个int给我们,这是因为TypeList类似链表的递归定义会把SomeType展开成

TYPELIST_4(int,unsigned int, char,unsigned char)

对上面这个TypeList求取第一个类型当然就是int。这曾经把我弄得哭笑不得,或许自己再写一些代码把其中的IntType封装一下再存入进去能解决问题,但是为此我花费了若干天时间研究该怎么写这个封装代码,而且越到后面越是被其复杂的递归弄得晕头转向。

于是我一不做二不休,直接抛弃TypeList,自己设计了TypeVector。

 

4.2 一个似曾相识却又崭新的世界

为了让TypeVector能存储其自身,我必须想办法抛弃递归定义的技法,由此

我想到了下面的代码:

//这里我们只设置了7个类型,你可以写更多,但是在我后面的程序用不了那么多

template< typename Type0 = NullType,typename Type1 = NullType,

typename Type2 = NullType,typename Type3 = NullType,

typename Type4 = NullType,typename Type5 = NullType,

typename Type6 = NullType >

struct TypeVector

{

           typedef Type0 Type0;

           typedef Type1 Type1;

           typedef Type2 Type2;

           typedef Type3 Type3;

           typedef Type4 Type4;

           typedef Type5 Type5;

           typedef Type6 Type6;

};

       TypeVector的形态和TypeList形成了鲜明对比,TypeVector更为简洁,而且最重要的是不需递归就可以直接从中取出任意一个类型。这个形态很像数组的定义,不过不一样的是它是用来存储类型的。

 

4.3 TypeVector的算法

当我实作出TypeVector之前完全没有想到会有如此大的收获,因为我仅仅是

能存储其自身而创作的它,可是我却发现其算法是如此简洁明了,和TypeList形成了鲜明对比,在定制算法时候很多都是复制粘贴的操作,虽然代码量看似很多,但是你肯定不用花很多时间在调试代码的正确性上。

       为了能兼容使用TypeList的程序,我采用了和TypeList同样的算法命名。

 

4.3.1 求取TypeVector长度

现在求取TypeVector的长度变得异常简单,只是一些模板的偏特化而已,一

眼就可看懂其算法思想。

//********************************************************************

//名称:  Length

//作用:  求取TypeVector中类型个数

//输入(模板参数): TVector,需要求取长度的TypeVector

//输出:  Length<TVector>::value 求取长度值

//算法思想:

//     根据TypeVector的特化方式判断出其类型个数。

//********************************************************************

template<typename TVector> struct Length;

 

template<>

struct Length<TypeVector<> >

{

    enum { value = 0 };

};

 

template<typename T0>

struct Length<TypeVector<T0> >

{

    enum { value = 1 };

};

 

template<typename T0,typename T1>

struct Length<TypeVector<T0,T1> >

{

    enum { value = 2 };

};

 

template<typename T0,typename T1,typename T2>

struct Length<TypeVector<T0,T1,T2> >

{

    enum { value = 3 };

};

 

template<typename T0,typename T1,typename T2,typename T3>

struct Length<TypeVector<T0,T1,T2,T3> >

{

    enum { value = 4 };

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4>

struct Length<TypeVector<T0,T1,T2,T3,T4> >

{

    enum { value = 5 };

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5>

struct Length<TypeVector<T0,T1,T2,T3,T4,T5> >

{

    enum { value = 6 };

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct Length<TypeVector<T0,T1,T2,T3,T4,T5,T6> >

{

    enum { value = 7 };

};

 

使用时候也是和TypeList一样的使用方法:

Length< TypeVector<int,unsigned int, char,unsigned char> >::value;

 

4.3.2 求TypeVector某个索引值处的类型

其实由于TypeVector类似数组的结构,我们可以很轻易的写出下面这个码:

typedef TypeVector<int,unsigned int,long int> IntType;

当我们要取用里面索引值第1个型别时候可以这样写:

typedef IntType::Type1 MyType;

 

    不过为了兼容TypeList的程序,我们还是要提供同样的接口,哪怕看似无用的。不过即便是这样,还是能从中学到一些神奇的技巧,请你别跳过这,耐心往下看。

//********************************************************************

//名称:  TypeAt

//作用:  求取TypeVector中某索引值处的类型

//输入(模板参数): TVector,需要求取的TypeVectori,索引值。

//输出:  TypeAt<TVector,i>::Result 求取长度值

//算法思想:

//     根据i数值不同做出不同的偏特化体

//********************************************************************

template <typename TVector , unsigned int i>

struct TypeAt

{

    typedef NullType Result;

};

 

template <typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,0>

{

    typedef T0 Result;

};

 

template <typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,1>

{

    typedef T1 Result;

};

 

template <typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,2>

{

    typedef T2 Result;

};

 

template <typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,3>

{

    typedef T3 Result;

};

 

template <typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,4>

{

    typedef T4 Result;

};

 

template <typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,5>

{

    typedef T5 Result;

};

 

template <typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>,6>

{

    typedef T6 Result;

};

 

    写完上面的代码以后我本没太多想法,后又得到网上一位前辈(网名Intercessor)提示发现其实一切都可以变得更酷,利用宏定义在编译期执行的动作,可以简化很多重复的代码。简化后的代码像这样:

//给一个宏定义,用##(连接符)连接序号,最后就可以做出所有偏特化体

#define META_TYPEAT(atPos)    template <typename T0,typename T1,typename T2 /

                        ,typename T3,typename T4,typename T5,typename T6>   /

                   struct TypeAt<TypeVector<T0,T1,T2,T3,T4,T5,T6>, atPos>   /

                  {                                                         /

                            typedef T##atPos Result;                       /

                  };

 

template <typename TVector , unsigned int i>

struct TypeAt

{

        typedef NullType Result;

};

    

     //后面就可以利用宏定义写出重复的代码。

META_TYPEAT(0)

   

META_TYPEAT(1)

   

META_TYPEAT(2)

   

META_TYPEAT(3)

   

META_TYPEAT(4)

   

META_TYPEAT(5)

   

META_TYPEAT(6)

 

当我们需要查找某个索引值处的类型时候,程序是这样的。

 

typedef TypeVector<int,unsigned int,long int> IntType;

typedef TypeAt<IntType,1>::Result MyType;

 

4.3.3 向TypeVector末端添加类型

类似前面的算法,向末端添加一个类型就相当于重新定义一个TypeVector并

返回,主要技巧还是模板的偏特化,下面给出代码:

//********************************************************************

//名称:  Append

//作用:  TypeVector末端添加类型

//输入(模板参数): TVector,需要添加的TypeVectorT,添加类型。

//输出:  Append<TVector,T>::Result 添加后得到的TypeVector

//算法思想:

//     根据TypeVector长度不同做出不同的偏特化体

//********************************************************************

 

template<typename TVector, typename T> struct Append;

 

template<typename T>

struct Append<TypeVector<>,T>

{

    typedef TypeVector<T> Result;

};

 

template<typename T0,typename T>

struct Append<TypeVector<T0>,T>

{

    typedef TypeVector<T0,T> Result;

};

 

template<typename T0,typename T1,typename T>

struct Append<TypeVector<T0,T1>,T>

{

    typedef TypeVector<T0,T1,T> Result;

};

 

template<typename T0,typename T1,typename T2,typename T>

struct Append<TypeVector<T0,T1,T2>,T>

{

    typedef TypeVector<T0,T1,T2,T> Result;

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T>

struct Append<TypeVector<T0,T1,T2,T3>,T>

{

    typedef TypeVector<T0,T1,T2,T3,T> Result;

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T>

struct Append<TypeVector<T0,T1,T2,T3,T4>,T>

{

    typedef TypeVector<T0,T1,T2,T3,T4,T> Result;

};

 

template<typename T0,typename T1,typename T2, typename T3,typename T4,typename T5,typename T>

struct Append<TypeVector<T0,T1,T2,T3,T4,T5>,T>

{

    typedef TypeVector<T0,T1,T2,T3,T4,T5,T> Result;

};

 

    当需要向一个TypeList末尾添加一个类型的时候代码像这样:

    typedef Append< TypeVector< int,unsigned int > ,long int >::Result IntType;

 

4.3.4 删除TypeVector中某索引值所在位置的类型

之所以先介绍这个删除算法是因为另外一个删除算法(删除第一次匹配

类型)需要依赖到这个算法

    这个算法同样很简单,只需要一些模板面特化技术就能实现。

//********************************************************************

//名称:  ErasePose

//作用:  删除TypeVector中某索引值所在类型

//输入(模板参数): TVector,需要添加的TypeVectori,索引值。

//输出:  ErasePose<TVector,i>::Result 删除后的TypeVector

//算法思想:

//     根据索引值i不同做出不同的偏特化体

//********************************************************************

template <typename TVector , int i>

struct ErasePose

{

    typedef TVector Result;

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,0>

{

    typedef TypeVector<T1,T2,T3,T4,T5,T6> Result;

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,1>

{

    typedef TypeVector<T0,T2,T3,T4,T5,T6> Result;

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,2>

{

    typedef TypeVector<T0,T1,T3,T4,T5,T6> Result;

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,3>

{

    typedef TypeVector<T0,T1,T2,T4,T5,T6> Result;

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,4>

{

    typedef TypeVector<T0,T1,T2,T3,T5,T6> Result;

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,5>

{

    typedef TypeVector<T0,T1,T2,T3,T4,T6> Result;

};

 

template<typename T0,typename T1,typename T2,typename T3,typename T4,typename T5,typename T6>

struct ErasePose<TypeVector<T0,T1,T2,T3,T4,T5,T6>,6>

{

    typedef TypeVector<T0,T1,T2,T3,T4,T5> Result;

};

    用法如下代码:

    typedef ErasePose< IntType,1 >::Result E1IntType;

 

4.3.5 得第一个匹配TypeVector中某类型的索引值

按照我们预先的希望,或者惯性思考,这个算法的代码也应该是若干重复的

模板偏特化而已,但是当实作出代码以后才发现原来没有那么简单,也许你会想到这个算法的代码类似这样:

template<typename TVector , typename T>

struct IndexOf

{

        enum {value = -1};

};

   

template<typename T>

struct IndexOf<TypeVector<T>,T>

{

        enum {value = 0};

};

   

template<typename T0,typename T>

struct IndexOf<TypeVector<T0,T>,T>

{

        enum {value = 1};

};

//...and more...

 

如果是这样,那当我写出这样的代码时候,将会出现错误结果。

 

typedef TypeVector<int,char,int,double> SomeType;

int index = IndexOf<SomeType,int>::value;

   

    编译器看到上面代码将会提示你,它无法判断该执行哪一个模板偏特化体,因为索引0位置和索引2位置的偏特化体都有条件得到执行(偏特化与你书写的先后顺序无关),这就是所谓的模绫两可。

    为了解决这个问题,我们只有能回到该死的偏特化递归去了,不过幸运的是毕竟不是每个算法都必须涉及到递归,否则我们这个TypeVector就没太多存在意义了。

    下面介绍正确的IndexOf算法

//********************************************************************

//名称:  IndexOf

//作用:  TypeVector中查找第一个匹配某一类型的索引值

//输入(模板参数): TVector,需要查找的TypeVectorT,所要查找的类型

//输出:  IndexOf<TVector,T>::value 查找后得到的索引值

//算法思想:

//     如果TVector为空,则定义value-1

//     否则

//            如果TVector第一个类型匹配T,则定义value0

//            否则

//                   删除TypeVector的第一个类型再做索引,并且把结果value赋值临时变//                   temp

//                   如果temp-1,则定义value-1

//                          否则

//                                 定义value temp + 1

//********************************************************************

template <typename TVector , typename T> struct IndexOf;

   

template<typename T>

struct IndexOf<TypeVector<> ,T>

{

        enum { value = -1};

};

   

template <typename TVector>

struct IndexOf<TVector,typename TypeAt<TVector,0>::Result>

{

        enum{ value = 0 };

};

   

template <typename TVector ,typename T>

struct IndexOf

{

private:

        enum{ temp = IndexOf<typename ErasePose<TVector,0>::Result,T>::value };

public:

        enum{ value = temp == -1 ? -1 : 1 + temp };

};

 

如果你需要索引某类型时候,程序代码像这样的:

typedef TypeVector<int,char,int,double> SomeType;

int index = IndexOf<SomeType,int>::value;

上面的index将得到0;

 

4.3.6 删除TypeVector中第一个匹配的类型

这个算法之所以在最后讲是因为需要用到前面算法的帮助,否则又将是让人

厌烦的模板偏特化递归调用。

//********************************************************************

//名称:  Erase

//作用:  删除TypeVector中查找第一个匹配的某类型

//输入(模板参数): TVector,需要删除的TypeVectorT,所要删除的类型

//输出:  Erase<TVector,T>::Result 删除后得到的TypeVector

//算法思想:

//     求出类型T的索引位置并赋值value,然后删除value索引所在位置的类型。

//********************************************************************

template<typename TVector,typename T>

struct Erase

{

        enum { value = IndexOf<TVector,T>::value };

        typedef typename ErasePose<TVector,value>::Result Result;

};

 

4.4 TypeVector结束语

 

TypeVector相信大家现在有所了解了,其以类似数组方式的存储形态使得其

支持随即读取其中某个类型,这在很大程度上简化了我们对应算法的复杂程度,而且其有一个优良的特性就是可以将TypeVector类型存储于TypeVector中,且最后可以调用IndexOf寻找出其索引值或者根据索引值调用TypeAt获得其类型,这在某些复杂类型情况下是很方便的。

    但是正所谓寸有所短,尺有所长。TypeVector相对于TypeList的缺点就是维护稍显困难,当我们因为程序需要而在库文件中扩展TypeList的长度时候我们并不需要修改其算法,但是如果我们修改TypeVector最大容量以后就必须修改其对应算法,不过值得高兴的是我们可能只需要一些复制粘贴动作就可以修改了,但是即便最小的修改也需要重新编译程序,这在某些时候显得异常麻烦。

    再好的算法也必须在最适合的环境下才能得到最大的效率,这也就是我同时介绍TypeList和TypeVector的关系,因为不一定其中一种就是最好的,只是看在什么环境下了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值