函数需要实现同一个类的功能时,通常通过以下几种方式实现,但他们往往有各种缺点:
1 .重载函数,为所需的相同行为的每个不同类型重新实现。
缺陷:出现新类型后,重新添加对应的函数。
代码复用率低
仅返回值类型不同无法解决函数重载
难以维护
2 .将共同代码放入共同的基类,通过继承实现
缺陷:无法进行参数检查
代码很难维护
3 .宏函数预处理器
缺陷:宏函数无法检测参数
容易发生宏观带来的各种危险
因此,引入函数模板。
函数模板:表示函数系列,它在使用时参数化,并基于实例类型生成函数的特定类型版本,而不管类型如何。
该函数模板与类型无关,在使用时被参数化,并根据实例类型生成函数的特定类型。
# define _ CRT _ secure _ no _ warnings1# includeiostreamusingnamespacestd; 模板类t//这里的类是typename (尽可能使用typename ) tadd (t left,T right ) ) { return left right; }int main () { cout Addint endl; 返回0; }上例是实现加法功能的函数模板。
即使我们不给出合适的类型,编译器也会自动识别该类型并调用与该类型对应的函数。
在模板定义中,模板参数不能为空
定义模板可以是模板或模板
但是,不能用结构代替typename。
如果在定义函数时用t定义变量a和b,很明显a和b的类型也是未知的,我们只需要实际参数就可以决定模板类型参数和返回值类型。
模板函数也可以定义为内联函数,但只能将inline定位在模板参数列表之后、返回值之前。
模板参数列表不能为空。
模板类t//这里的类是typenameinlinetadd(tleft,T right ) { return left right; }模板不是类或函数。 编译器使用模板生成特定类型的类和函数,并将其作为模板函数的实例化。
模板参数只能在模板参数之后和模板声明或定义末尾之间使用,并且遵循名称掩码规则。
类型参数:参数类型很模糊,需要在实参中实例化。
非类型参数:参数的类型是确定的
调用非类型参数的函数时,必须引用或显式调用。
模板型参数的名称在同一模板型参数列表中只能使用一次。
# define _ CRT _ secure _ no _ warnings1# includeiostreamusingnamespacestd; 模板类t//这里的类是typenametadd(tleft,T right ) cout typeid (left ).name ) ) endl; 返回左右光; }intadd(intleft,int right ) { return left right; }int main () coutaddint ) 1,2 ) endl; coutadd(1.5、2.5 ) endl; cout add (3,4 ) endl; coutadd(5,) int (6.1 ) endl; //显示类型转换return 0; }
非模板函数可以与同名的模板函数同时存在。
在相同条件下调用时,非模板函数优先调用不是编译器生成的函数
如果条件相同,则必须显式调用模板函数。 即,add(a,b );
显式指定空模板参数列表。 此语法指示编译器只有模板与此调用相匹配,如果所有模板参数都需要根据名为max(a,b )的实参数进行演绎,则调用模板函数,参数类型来自实参数。
模板函数不支持隐式类型转换,但常规函数可以
导出参数时,编译器不进行类型转换,但有两种特殊情况。
1.const类型可以用非const (引用/指针)调用
2 .数组类型/函数——指针
在程序运行期间,模板一共编译了两次:
1 .实例化前,检查模板是否语法错误
2 .实例化时,检测模板代码,调用是否有效
函数模板支持重载。
# define _ CRT _ secure _ no _ warnings1# includeiostre
am>using namespace std;template<class T>//这里的class也可替换为typenameT Add(T left, T right){ cout << typeid(left).name() << endl; return left + right;}template<typename T>T Add(T first, T second, T third){ cout << typeid(left).name() << endl; return first + second + third;}int main(){ cout << Add<int>(1, 2) << endl; //cout << Add<>(1.5, 2.5) << endl; cout << Add<int>(3, 4, 5) << endl; //cout << Add(5, (int)6.1) << endl;//显示类型转换 return 0;}函数模板的特化
有的时候,通用的模板并不适用于某些类型,这时候就需要对函数模板进行特化。
template<typename T>T Max(T left, T right){ return left > right ? left : right;}int main(){ char* s1 = "dabc"; char* s2 = "adac"; cout << Max(s1, s2) << endl; return 0;}在上例中,本来的输出结果应该是dabc,但是输出的结果却是adac,这是因为这里比较的并不是两个字符串,而是两个指针的地址大小。
template<typename T>int Max(const T left, const T right){ return left > right ? left : right;}template<>int Max<char*>(char *const left, char *const right){ return strcmp(left, right);} 1.模板函数必须已经存在 2.和已存在的模板函数的类型必须保持一致类模板
template<typename T>class List{public: List() { cout << "template" << endl; }};int main(){ List<int> l; return 0;}1、模板函数不允许有缺省参数而类模板确是支持的
2、用类模板定义对象的时候必须在类模板名之后在尖括号内指定实际的类型名
全特化
偏特化