特化与偏特化
函数模板特化
模板参数在某种特定类型下的具体实现称为模板的特化
函数模板特化:
- 关键字template后面接一对<>
- 再接模板名和一对<>,尖括号中定义这个特华的模板参数
template <typename T> int com(const T &v1,const T &v2) { return v1>v2?1:0; } template <> int com<const string>(const string &v1,const string &v2) { return strcmp(v1.c_str(),v2.c_str()); }
特化的声明必须与对应的模板相匹配,当调用cmp函数时,传给他两个const string类型的参数,编译器调用特化版本,特化函数参数固定为const string类型,当调用其他类型(包括string)时,调用泛化版本
1.声明特化模板
函数特化模板可以声明而无需定义
template <> int com<const string>(const string &v1,const string &v2)
注意:
- 模板特化总是包含模板空参说明符(template<>)
- 必须包含函数形参列表,如果可以从函数形参推断模板实参,不必显示定义模板实参
template <> int com(const string &v1,const string &v2) { return strcmp(v1.c_str(),v2.c_str()); }
2.函数重载与模板特化
如果省略template<>,则是函数的重载非模板版本
template <typename T> int com(const T &v1,const T &v2) { return v1>v2?1:0; } //template <> int com(const string &v1,const string &v2) { return strcmp(v1.c_str(),v2.c_str()); }
注意:
当定义非模板函数的时候,对实参应用常规转换;特化模板的时候,对应的实参类型不应用转换。
模板特化版本的调用中,实参类型必须与特化版本的函数类型参数完全匹配,若果不完全匹配,编译器将为实参从模板定义实例化一个实例。
类模板特化
特化可以定义与模板本身不同的成员。如果一个特化无法从模板定义某个成员,该特化类型就不能使用该成员。类模板的定义不会用于显示创建特化成员的定义。
1.类特化定义
在类特化外部定义成员时,成员前不加template<>
/* *类特化为 const char*类型,上面的函数特化类似 *此例子只是为了说明类特化模板定义成员 */ void queue<const char*>::push(const char *val) { return real_queue.push(val); }
2.特化成员而不特化类
template<> void queue<const char*>::push(const char *val);
与任何特化函数模板一样,以空参的形参表开头,在定义类的头文件中
3.类模板的偏特化
模板偏特化(template partitial specialization)是模板特化的一种特殊情况,指显示指定部分模板参数而非全部模板参数,或者指定模板参数的一部分而非全部特性,也称为模板部分特化
template <class T1,class T2> class some_templae { // } //特化T2为int类型,T1为任何类型 template<T1> class some_template<T1,int> { // }
注意:
- 偏特化像类模板的定义
- 偏特化的模板形参是类模板定义的形参表子集
- 偏特化形参表只列出未知模板形参的那些实参
#include <iostream> #include <cstring> using namespace std; template <class T1,class T2> class Ship { public: Ship(T1 a1,T2 a2):a(a1),b(a2){} void show() { cout<<a<<" "<<b<<endl; } private: T1 a; T2 b; }; //特化T2为int类型,T1为任何类型 template<class T1> class Ship<int,T1>//<T1,int>都可以,int和T1的位置可以交换 { public: Ship(T1 a1,int a2):a(a1),b(a2){} void show() { cout<<a<<" "<<b<<endl; } private: T1 a; int b; }; int main() { Ship<double,int> s(1.222,2);//调用通用版本,也可调用通用版本以为当生命部分特化时,编译器会选择最特化的版本,当无部分特化时,选择通用版本 Ship<double,double> s2(6.66,9.99) ;//调用特化版本 s.show(); s2.show(); return 0; }
注意:
部分特化的定义与通用模板的定义不冲突,部分特化可以具有与通用模板完全不同的成员集合,类模板成员的通用定义永远不会用实例化模板部分的特化成员
重载与函数模板
(1)为该函数建立候选集合:
a.与背调函数名相同的任意普通函数
b.任意模板实例化,模板实参发现了与调用中所用函数参数相匹配的模板实参
(2)确定哪些普通函数的的行为是可行的,候选集合中的每个实例模板都可行
(3)如果需要转换来进行调用,根据转换的种类排列可行函数,模板函数实例转换有限
a.如果只有一个函数可选,就调用他
b.有多个可选,具有二义性,去掉所有模板实例
(4)重新排列去掉函数模板实例的可用函数
a.有一个则调用
b.否则具有二义性
- 如果只包含一个函数可选,就调用这个函数
- 否则具有二义性
//函数模板 template<typename T> int com(const T&,const T&); //普通函数 int com(const char*,const char*); char s1[]="sdfa",s2[]="dfaskf"; com(s1,s2);//会调用普通函数,因为函数会将数组转化为指针,调用普通函数优先于调用模板
转换与重载函数模板
当普通函数与模板函数都同样匹配,且一样好时,非模板版本优先