一、函数模板的定义
函数模板的定义和声明放在一起,前面需要加一行:
template<typename T1 ,...... ,typename TN>
T表示类型待定,我们在使用模板时需要明确类型。
模板标识从template开始定义,后续跟着一对“<>”,中间是参数列表。模板参数表示等待绑定实际类型的模板类型列表,统一表示为“typename”类型,或者是“class”类型。
注意1、一旦声明了template,则后续模板参数列表不能为空。
注意2、模板参数列表,可以指定函数参数类型,也可以指定返回值类型,也可以指定函数局部变量类型。
一个典型的使用实例:
#include <iostream>
using namespace std;
class Student{
private:
string name;
public:
Student(string name){
this->name=name;
}
void display(){
cout<<"I am "<<name<<endl;
}
};
template<typename M>
M f(M m1, M m2){ //模板参数作为函数参数和函数返回值
if(m1<m2){
return m1;
} else{
return m2;
}
}
template<typename M> //模板参数作为局部变量
void f1(){
M m("Tom");
m.display();
}
int main(){
cout<<f(1,2)<<endl;
cout<<f(1.2,3.2)<<endl;
string a="abc";
string b="abd";
cout<<f(a,b)<<endl;
cout<<f('a','b')<<endl;
f1<Student>();
return 0;
}
输出:
1
1.2
abc
a
I am Tom
这里有个小坑,比较字符串大小时候,如果这么写:
#include <iostream>
using namespace std;
template<typename M>
int f(M m1, M m2){
if(m1<m2){
return 1;
} else{
return -1;
}
}
int main(){
cout<<f("abc","abd")<<endl;
return 0;
}
可能会有问题。因为“abc”底层是指针,我当成string用,是因为string有对应的转换构造函数。这是模板,编译器不知道应该转换为什么类型,所以不会调用转换构造函数,直接是两个地址比较大小。
注意:函数模板定义,模板参数不能为空。
二、函数模板的具体化
有如下原则:
(1)用户使用时,必须告诉编译器,每一个模板参数实例化为某一个类型。
(2)可以显示指定类型,如下:
cout<<f<int>(1,2)<<endl;
也可以不显示指定,隐式通过传入的类型让编译器推断。
(3)可以显示指定与隐式指定同时进行,但是要注意,显示指定的类型是按序匹配。
三、模板的编译
(1)编译器看到模板的时候,不进行编译,而是在使用(实例化)的时候才进行编译。
(2)模板函数的声明和定义都放到头文件中。