赞
踩
在学习模板的时候我们用模板来解决了一个add模板函数,实现不同类型的传参相加,实践证明,模板函数比普通函数好用。那么现在如果我们要新增一个需求,就是如果传入的是两个string类型的参数,我们不要简单的拼接,我们要在两个字符串之间添加一个空格,显然模板函数已经无法满足我们的要求,解决方法就是使用模板特化,简单说就是模板的一个特殊化,当传参为两个string类型的时候,不调用模板函数,而是调用特化模板函数,传入其它类型的时候,仍然使用模板函数,如下示例:
- template <typename T>
- T add(T a, T b)
- {
- return a+b;
- }
- template<> string add(string a,string b)
- {
- string c=" ";
- return a+c+b;
- }
- int a=5,b=6;
- string e="hu",f="daizhou";
- cout<< add(a,b)<<endl;
- cout<< add(e,f)<<endl;
模板特化的实现类似于函数重载,模板特化发生在编译时,而非运行时,所以效率高。
特化的模板声明,前面一般是template<>,方括号为空。当同时出现同名、同参的普通函数和特化模板函数,及同名模板函数时,编译器优先使用普通函数,再是特化模板函数,最后才是模板函数。
特化分为全特化和偏特化,全特化即把模板的所有参数都指定类型,示例如下,定义了一个类和他的全特化类,值得注意的是,泛化模板类的类名后面不需要类型,而特化模板需要在类名后面指定类型。
- template<typename T1,typename T2> //泛型模板,实体省略
- template<> class peple<int,int> //全特化
- {
- public:
- void print(int a,int b)
- {
- cout<<"typename<> a = "<< a <<" b= "<< b <<endl;
- }
- };
偏特化就是特化部分参数,如下示例,泛型模板2个参数都是模板,特化模板则将第二个参数固定,第一个模板仍然泛化。其实就是一个模板类中有多个参数类型,这些参数用到了模板参数,也用到了普通类型。
- template<typename T1,typename T2> class peple //泛型模板,实体省略
- template< typename T > class peple<T,int> //部分特化模板
- {
- public:
- void print(T a,int b)
- {
- cout<<"typename<> a = "<< a <<" b= "<< b <<endl;
- }
- };
如下示例,将第二个参数固定为普通类型的指针,第一个参数仍然是泛化模板类型,无论是什么类型的特化,模板类的类型指定与内部函数的传参类型无直接关联,如下列中,第2个参数是指针,但内部的print函数可有可无形参,即使有,也可以不同。实际上这种偏特化完全可以将指针替换成引用,或者前面添加const,有const和没const的匹配是不同。
- template<typename T> class peple<T, double *>
- {
- public:
- void print(int a,double b)
- {
- cout<<"typename<> a = "<< a <<" b= "<< b <<endl;
- }
- };
模板特化,指模板类的类型是另外一个模板类,如array、vector、list等。
- template<typename T> class peple<vector<T>>
- {
- public:
- void print(void)
- {
- cout<<"peple<T1 a,array<T2,int k>" << endl;
- }
- }
-
- int main(void)
- {
- peple<vector<int> > aa;
- aa.print();
- }
函数重载和模板函数重载有些类,特化的调用的特点是在函数后面用<>指定传参类型。有一个事实,就是模板函数仅支持全特化但是不支持偏特化,理由是因为函数重载先出现,而函数重载就已经能够实现函数偏特化的效果,一旦两个都支持,那就涉及优先级的问题,所以c++作者的选择是不允许函数模板偏特化。函数模板重载如下:
- template<typename T1> void func(T1 a)
- {
- cout<< "func1 a= "<< a << endl;
- }
-
- template<typename T1> void func(T1 *a)
- {
- cout<<"func2 *a= " << *a<<endl;
- }
- double c=5.6,d=7.8;
- func<double>(c);
- func<double *> (&d);//不支持偏特化
第①步:匹配非模板函数,即普通函数,如果匹配到就执行,匹配不到进行下一步;
第②步:匹配基础泛化版本函数,匹配不到就报错,匹配到了再进入下一步。
第③步:匹配全特化函数,如果有就执行,无就执行上一步的基础泛化版本。
细节:模板函数的特化版本,不参与函数重载,即可以有完全特化版本的函数与普通函数同名、同参,在调用的时候,普通调用就是func(a);而泛化版本的调用时func<int>(a);
到此我们特化的研究就暂告于段落,特化实际上是我们帮编译器去做了类型推导的工作,告诉编译器不用去推导了,直接使用我给出的这个。
模板特化和模板实例化两个概念注意区分,模板实例化指的是具体确定模板参数的类型T,模板特化就是前面一直在学的给出具体类型,本质上模板全特化就是一种实例化,而偏特化则不是,偏特化只是把模板类型的可能性压缩了,如模板偏特化成了指针,那么后面匹配的时候就只能是一个指针类型,因为模板函数并不是实例,普通函数是一个实例,所以他们不会冲突。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。