C++ 模版与特化的详解
扫描二维码
随时随地手机看文章
模版与特化的概念
函数模版与类模版
C++中模板分为函数模板和类模板
函数模板:是一种抽象函数定义,它代表一类同构函数。
类模板:是一种更高层次的抽象的类定义。
特化的概念
所谓特化,就是将泛型的东东搞得具体化一些,从字面上来解释,就是为已有的模板参数进行一些使其特殊化的指定,使得以前不受任何约束的模板参数,或受到特定的修饰(例如const或者摇身一变成为了指针之类的东东,甚至是经过别的模板类包装之后的模板类型)或完全被指定了下来。
模板特化的分类
针对特化的对象不同,分为两类:函数模板的特化和类模板的特化
函数模板的特化
当函数模板需要对某些类型进行特化处理,称为函数模板的特化。
类模板的特化
当类模板内需要对某些类型进行特别处理时,使用类模板的特化。
特化整体上分为全特化和偏特化
* 全特化
就是模板中模板参数全被指定为确定的类型。 全特化也就是定义了一个全新的类型,全特化的类中的函数可以与模板类不一样。
1234
偏特化
就是模板中的模板参数没有被全部确定,需要编译器在编译时进行确定。
全特化的标志就是产生出完全确定的东西,而不是还需要在编译期间去搜寻适合的特化实现,貌似在我的这种理解下,全特化的 东西不论是类还是函数都有这样的特点,
模板函数只能全特化,没有偏特化(以后可能有)。
模板类是可以全特化和偏特化的。
template <>然后是完全和模板类型没有一点关系的类实现或者函数定义,如果你要说,都完全确定下来了,那还搞什么模板呀,直接定义不就完事了?
但是很多时候,我们既需要一个模板能应对各种情形,又需要它对于某个特定的类型(比如bool)有着特别的处理,这种情形下特化就是需要的了。
全特化的标志:template <>然后是完全和模板类型没有一点关系的类实现或者函数定义偏特化的标志:template函数模版特化
目前的标准中,模板函数只能全特化,没有偏特化
至于为什么函数不能偏特化,似乎不是因为语言实现不了,而是因为偏特化的功能可以通过函数的重载完成。
函数模版的特化技巧
函数模板的特化:当函数模板需要对某些类型进行特别处理,称为函数模板的特化。
例如,我们编写了一个泛化的比较程序
templateint compare(const T &left, const T&right) { std::cout <<"in template..." <<std::endl; return (left - right); }
123456
这个函数满足我们的需求了么,显然不,它支持常见int, float等类型的数据的比较,但是不支持char*(string)类型。
所以我们必须对其进行特化,以让它支持两个字符串的比较,因此我们实现了如下的特化函数。
template < > int compare(const char* left, const char* right) { std::cout <<"in special template< >..." <<std::endl; return strcmp(left, right); }
1234567
也可以
template < > int compare(const char* left, const char* right) { std::cout <<"in special template< >..." <<std::endl; return strcmp(left, right); }
1234567示例程序1–比较两个数据
#include#include/// 模版特化 templateint compare(const T left, const T right) { std::cout <<"in template..." <<std::endl; return (left - right); } // 这个是一个特化的函数模版 template < >int compare(const char* left, const char* right) { std::cout <<"in special template< >..." <<std::endl; return strcmp(left, right); } // 特化的函数模版, 两个特化的模版本质相同, 因此编译器会报错 // error: redefinition of 'int compare(T, T) [with T = const char*]'| //template < >//int compare(const char* left, const char* right) //{ // std::cout <<"in special template< >..." <<std::endl; // // return strcmp(left, right); //} // 这个其实本质是函数重载 int compare(char* left, char* right) { std::cout <<"in overload function..." <<std::endl; return strcmp(left, right); } int main( ) { compare(1, 4); const char *left = "gatieme"; const char *right = "jeancheng"; compare(left, right); return 0; }
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
函数模版的特化,当函数调用发现有特化后的匹配函数时,会优先调用特化的函数,而不再通过函数模版来进行实例化。
示例程序二-判断两个数据是否相等
#include#includeusing namespace std; //函数模板 templatebool IsEqual(T t1,T t2){ return t1==t2; } template<> //函数模板特化 bool IsEqual(char *t1,char *t2){ return strcmp(t1,t2)==0; } int main(int argc, char* argv[]) { char str1[]="abc"; char str2[]="abc"; cout<<"函数模板和函数模板特化"<<endl; cout<<IsEqual(1,1)<<endl; cout<<IsEqual(str1,str2)<<endl; return 0; }
1234567891011121314151617181920212223242526类模版特化
类模板的特化:与函数模板类似,当类模板内需要对某些类型进行特别处理时,使用类模板的特化。例如:
这里归纳了针对一个模板参数的类模板特化的几种类型
一是特化为绝对类型;
二是特化为引用,指针类型;
三是特化为另外一个类模板。
这里用一个简单的例子来说明这三种情况:
特化为绝对类型
也就是说直接为某个特定类型做特化,这是我们最常见的一种特化方式, 如特化为float, double等
#include#include#include// general version templateclass Compare { public: static bool IsEqual(const T& lh, const T& rh) { std::cout <<"in the general class..." <<std::endl; return lh == rh; } }; // specialize for float template<>class Compare{ public: static bool IsEqual(const float& lh, const float& rh) { std::cout <<"in the float special class..." <<std::endl; return std::abs(lh - rh) < 10e-3; } }; // specialize for double template<>class Compare{ public: static bool IsEqual(const double& lh, const double& rh) { std::cout <<"in the double special class..." <<std::endl; return std::abs(lh - rh) < 10e-6; } }; int main(void) { Comparecomp1; std::cout <<comp1.IsEqual(3, 4) <<std::endl; std::cout <<comp1.IsEqual(3, 3) <<std::endl; Comparecomp2; std::cout <<comp2.IsEqual(3.14, 4.14) <<std::endl; std::cout <<comp2.IsEqual(3, 3) <<std::endl; Comparecomp3; std::cout <<comp3.IsEqual(3.14159, 4.14159) <<std::endl; std::cout <<comp3.IsEqual(3.14159, 3.14159) <<std::endl; return 0; }
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
如果期望使用偏特化,那么
templateclass A { } templateclass A{ }
123456789特化为引用,指针类型
templatestruct iterator_traits { typedef typename _Iterator::iterator_category iterator_category; typedef typename _Iterator::value_type value_type; typedef typename _Iterator::difference_type difference_type; typedef typename _Iterator::pointer pointer; typedef typename _Iterator::reference reference; }; // specialize for _Tp* templatestruct iterator_traits{ typedef random_access_iterator_tag iterator_category; typedef _Tp value_type; typedef ptrdiff_t difference_type; typedef _Tp* pointer; typedef _Tp& reference; }; // specialize for const _Tp* templatestruct iterator_traits{ typedef random_access_iterator_tag iterator_category; typedef _Tp value_type; typedef ptrdiff_t difference_type; typedef const _Tp* pointer; typedef const _Tp& reference; };
12345678910111213141516171819202122232425262728
当然,除了T*, 我们也可以将T特化为 const T*, T&, const T&等,以下还是以T*为例:
// specialize for T* templateclass Compare{ public: static bool IsEqual(const T* lh, const T* rh) { return Compare::IsEqual(*lh, *rh); } };
12345678910
这种特化其实是就不是一种绝对的特化, 它只是对类型做了某些限定,但仍然保留了其一定的模板性,这种特化给我们提供了极大的方便, 如这里, 我们就不需要对int*, float*, double*等等类型分别做特化了。
这其实是第二种方式的扩展,其实也是对类型做了某种限定,而不是绝对化为某个具体类型,如下:
// specialize for vectortemplateclass Compare<vector> { public: static bool IsEqual(const vector& lh, const vector& rh) { if(lh.size() != rh.size()) return false; else { for(int i = 0; i < lh.size(); ++i) { if(lh[i] != rh[i]) return false; } } return true; } };
123456789101112131415161718
这就把IsEqual的参数限定为一种vector类型, 但具体是vector还是vector, 我们可以不关心, 因为对于这两种类型,我们的处理方式是一样的,我们可以把这种方式称为“半特化”。
当然, 我们可以将其“半特化”为任何我们自定义的模板类类型:
// specialize for any template class type templatestruct SpecializedType { T1 x1; T1 x2; }; templateclass Compare<SpecializedType> { public: static bool IsEqual(const SpecializedType& lh, const SpecializedType& rh) { return Compare::IsEqual(lh.x1 + lh.x2, rh.x1 + rh.x2); } };
12345678910111213141516
这就是三种类型的模板特化, 我们可以这么使用这个Compare类:
// int int i1 = 10; int i2 = 10; bool r1 = Compare::IsEqual(i1, i2); // float float f1 = 10; float f2 = 10; bool r2 = Compare::IsEqual(f1, f2); // double double d1 = 10; double d2 = 10; bool r3 = Compare::IsEqual(d1, d2); // pointer int* p1 = &i1; int* p2 = &i2; bool r4 = Compare::IsEqual(p1, p2); // vectorvectorv1; v1.push_back(1); v1.push_back(2); vectorv2; v2.push_back(1); v2.push_back(2); bool r5 = Compare<vector>::IsEqual(v1, v2); // custom template class SpecializedTypes1 = {10.1f,10.2f}; SpecializedTypes2 = {10.3f,10.0f}; bool r6 = Compare<SpecializedType>::IsEqual(s1, s2);