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.编译器是通过寻找最合适的匹配选项来匹配选择实例化的类型