C++模板之特化与偏特化详解
前言
说到C++模板,这个已经不是什么新东西了,自己在实际开发中也用过;对于C++模板特化和偏特化,对于别人来说,已经不是什么新东西了,但是对于我来说,的确是我的盲区,那天在群里讨论这个问题,自己对于这部分确实没有掌握,又联想到在《STL源码剖析》一书中,对于此也是有着介绍。所以,今天就对此进行详细的总结,以备后忘。
C++模板
说到C++模板特化与偏特化,就不得不简要的先说说C++中的模板。我们都知道,强类型的程序设计迫使我们为逻辑结构相同而具体数据类型不同的对象编写模式一致的代码,而无法抽取其中的共性,这样显然不利于程序的扩充和维护。C++模板就应运而生。C++的模板提供了对逻辑结构相同的数据对象通用行为的定义。这些模板运算对象的类型不是实际的数据类型,而是一种参数化的类型。C++中的模板分为类模板和函数模板。
类模板如下:
#include<iostream> usingnamespacestd; template<classT> classTClass { public: //TClass的成员函数 private: TDateMember; };
函数模板如下:
template<classT> TMax(constTa,constTb) { return a>b?a:b; }
模板特化
有时为了需要,针对特定的类型,需要对模板进行特化,也就是所谓的特殊处理。比如有以下的一段代码:
#include<iostream> usingnamespacestd; template<classT> classTClass { public: boolEqual(constT&arg,constT&arg1); }; template<classT> boolTClass<T>::Equal(constT&arg,constT&arg1) { return(arg==arg1); } intmain() { TClass<int>obj; cout<<obj.Equal(2,2)<<endl; cout<<obj.Equal(2,4)<<endl; }
类里面就包括一个Equal方法,用来比较两个参数是否相等;上面的代码运行没有任何问题;但是,你有没有想过,在实际开发中是万万不能这样写的,对于float类型或者double的参数,绝对不能直接使用“==”符号进行判断。所以,对于float或者double类型,我们需要进行特殊处理,处理如下:
#include<iostream> usingnamespacestd; template<classT> classCompare { public: boolIsEqual(constT&arg,constT&arg1); }; //已经不具有template的意思了,已经明确为float了 template<> classCompare<float> { public: boolIsEqual(constfloat&arg,constfloat&arg1); }; //已经不具有template的意思了,已经明确为double了 template<> classCompare<double> { public: boolIsEqual(constdouble&arg,constdouble&arg1); }; template<classT> boolCompare<T>::IsEqual(constT&arg,constT&arg1) { cout<<"CallCompare<T>::IsEqual"<<endl; return(arg==arg1); } boolCompare<float>::IsEqual(constfloat&arg,constfloat&arg1) { cout<<"CallCompare<float>::IsEqual"<<endl; return(abs(arg-arg1)<10e-3); } boolCompare<double>::IsEqual(constdouble&arg,constdouble&arg1) { cout<<"CallCompare<double>::IsEqual"<<endl; return(abs(arg-arg1)<10e-6); } intmain() { Compare<int>obj; Compare<float>obj1; Compare<double>obj2; cout<<obj.IsEqual(2,2)<<endl; cout<<obj1.IsEqual(2.003,2.002)<<endl; cout<<obj2.IsEqual(3.000002,3.0000021)<<endl; }
模板偏特化
上面对模板的特化进行了总结。那模板的偏特化呢?所谓的偏特化是指提供另一份template定义式,而其本身仍为templatized;也就是说,针对template参数更进一步的条件限制所设计出来的一个特化版本。这种偏特化的应用在STL中是随处可见的。比如:
template<class_Iterator> structiterator_traits { typedeftypename_Iterator::iterator_categoryiterator_category; typedeftypename_Iterator::value_type value_type; typedeftypename_Iterator::difference_type difference_type; typedeftypename_Iterator::pointer pointer; typedeftypename_Iterator::reference reference; }; //specializefor_Tp* template<class_Tp> structiterator_traits<_Tp*> { typedefrandom_access_iterator_tagiterator_category; typedef_Tp value_type; typedefptrdiff_t difference_type; typedef_Tp* pointer; typedef_Tp& reference; }; //specializeforconst_Tp* template<class_Tp> structiterator_traits<const_Tp*> { typedefrandom_access_iterator_tagiterator_category; typedef_Tp value_type; typedefptrdiff_t difference_type; typedefconst_Tp* pointer; typedefconst_Tp& reference; };
看了了么?这就是模板偏特化,与模板特化的区别在于,模板特化以后,实际上其本身已经不是templatized,而偏特化,仍然带有templatized。我们来看一个实际的例子:
#include<iostream> usingnamespacestd; //一般化设计 template<classT,classT1> classTestClass { public: TestClass() { cout<<"T,T1"<<endl; } }; //针对普通指针的偏特化设计 template<classT,classT1> classTestClass<T*,T1*> { public: TestClass() { cout<<"T*,T1*"<<endl; } }; //针对const指针的偏特化设计 template<classT,classT1> classTestClass<constT*,T1*> { public: TestClass() { cout<<"constT*,T1*"<<endl; } }; intmain() { TestClass<int,char>obj; TestClass<int*,char*>obj1; TestClass<constint*,char*>obj2; return0; }
对于输出结果,我这里就不写了,大家可以试一试。
特化与偏特化的调用顺序
对于模板、模板的特化和模板的偏特化都存在的情况下,编译器在编译阶段进行匹配时,是如何抉择的呢?从哲学的角度来说,应该先照顾最特殊的,然后才是次特殊的,最后才是最普通的。编译器进行抉择也是尊从的这个道理。从上面的例子中,我们也可以看的出来,这就就不再举例说明。
总结
对于模板的特化和偏特化,我的理解可能也不是很正确。希望大家和我进行探讨。我这里只是对自己的一些理解进行了总结。最后,也希望大家对我的博客提出中肯的建议。我坚信,分享使我们更进步。