• C++编程新思维中的技巧


     1.编译器断言

    技巧大致跟后面的一样,都是利用偏特化,但是在C++ 0X里面已经有static_assert,所以感觉这东西也没什么用处了,更多的只是开阔眼界
    2.偏特化
    就是专门对一个类型去进行特殊的处理,毕竟template会给生成所有的类型相同的操作,但是有时候我们需要对特定的几个类型去处理,于是就有了特化和偏特化
    3.局部类
    一直被忽略的C++的语法,可以直接在类或者函数里面定义类,不过一直被忽视就对了,感觉这个还是挺有意思的
    4.常数映射为型别:
    template <int v>
    struct Int2Type
    {
         enum
         {
              value = v;
         }
    }
    这样的话,每次用不同的数字去实例化Int2Type,都是不同的类型
    书中举了个反例
     1 <class T,bool flag>
     2 class myclass
     3 {
     4      void DoSomething ()
     5     {
     6           if(flag )
     7          {
     8               T *new_obj = p_old_obj-> clone();
     9          }
    10           else
    11          {
    12               T *new_obj = new T(p_olb_obj );
    13          }
    14     }
    15 };

     
     
    上面的程序有两个问题
    1.如果T的类型把复制构造函数声明为private或者protect,则编译错误
    2.如果p_old_obj没事实现clone,则编译错误
     
    我们可以这样改造下面的程序:
    template <class T, bool isPolymorphic >
    class NiftyContainer
    {
    private:
         void DoSomething (T * pobj,int2type <true>)
        {
              T *pNewObj = pobj-> clone();
        }
     
         void DoSomething (T * pobj,int2type <false>)
        {
              T *pNewObj = new T(pobj );
        }
     
    public:
         void DoSomething (T * pobj)
        {
             DoSomething(pobj ,int2type< isPolymorhpic>());
        }
    };

      
    这样,只要你的程序有实现clone或者将复制构造函数设置为public,只要符合其中一个条件,就可以编译通过,而且选择什么动作是编译的时候就决定的
    之所以只要实现clone或者public了复制构造函数就可以通过,是因为编译器不会去编译一个未用到的类成员函数
    5.型别对型别的映射
    在我们编程的时候,我们经常需要创建一个函数,用以产生一个新对象
    template <class T, class U >
    T *create (const U &arg )
    {
         return new T( arg);
    }

      
    那如果现在需要构造一个widget对象,但是这个对象需要-1作为构造参数,怎么办??
    我们可以利用type2type来让编译器去选择我们要使用哪个模板函数
    template <class T, class U >
    T *create (const U &arg ,type2type< T>)
    {
         return new T( arg);
    }
     
    template <class U>
    widgets *create (const & arg,type2type <widgets>)
    {
         return new widgets( arg,-1);
    }

      
    6,型别选择
    有了前面的基础,看到后面的几个也就轻松,原理跟3(型别映射型别差不多)
    其实就是利用函数的偏特化机制
    template <bool flag, class T ,class U>
    struct select
    {
         typedef T result;
    };
     
    template <class T, class U >
    struct select <false, T,U >
    {
         typedef U result;
    };

    我们可以根据flag来在编译的时候就决定是要使用T还是使用U类型,不过这本书中的例子是用来选择是使用指针还是使用普通的类型,感觉这个例子没有traits好用
     
    7.在编译期的时候确定一个类型是否可以转换成为另外一个类型(这样说感觉不怎么准确,更准确地说类型是否会隐式转换为另外一个类型)
     
    template <class T, class U >
    class conver
    {
         typedef char small;
        
         class big
        {
              char dummy [2];
        };
        
         static small test( U);
        
         static big test(...);
        
         static T makeT();
    public:
         enum
        {
              exists = sizeof ( test( makeT() ) ) == sizeof(small )
        };
    };

     
    这样,我们在写程序的时候,可以直接这样来判断一个类是否可以转换成另外一个类
    算起来这个算是我觉得比较有趣的模板技巧了
     
    1.实际上smalltest和bigtest还有makeT都没有实例化出来,因为sizeof并不会有实例化操作,所以即使没有定义函数,依然通过编译
     
    2.多声明了一个makeT函数是因为一些类会把构造函数声明为私有的,如果不使用makeT,那么将无法调用T()
     
    3.模板会寻找最佳匹配条件,也就是说,如果我们再多声明一个类型Z,把static big test(...)改成static big test(Z),那么在我们程序编译的时候,即使两个类型能够互相转换,比如int和size_t,那么编译器将会去调用test(Z),这样将无法看出两个类是否可以互相转换,反之,如果test是(...)这样声明的,那么编译器会认为test(U)比test(...)匹配,于是就去使用test(U)函数
     
    8.type_info的外覆类
    这个倒没什么,无非就是type_info无法复制,所以重新写了一个类包装下
     
    9.定义Nulltype和emptyType复制类
    也没什么好说的
     
    10.traits
    在前面的文章已经说过了
    总的来说,要理解这些技巧,需要理解几点:
    1.模板的特化
    2.编译器是通过寻找最合适的匹配选项来匹配选择实例化的类型
     
  • 相关阅读:
    JAVA面向对象初步知识总结:封装、继承、多态
    最短路径算法
    Retrofit源码分析(一)
    属性动画总结(纯搬运郭霖先生的文章)
    多项式相关算法模板
    二分图匹配问题
    一般图匹配
    三种上下界网络流模板
    最大流模板
    2017-2018 Petrozavodsk Winter Training Camp, Saratov SU Contest
  • 原文地址:https://www.cnblogs.com/linyilong3/p/3380745.html
Copyright © 2020-2023  润新知