一、模板是什么?
所谓模板(templates),就是针对“一个或者多个尚未明确的型别”所撰写的函数或者类别,使用模板时,可以显式地(explicitly)或隐式(implicitly)将型别当作参数来传递。下面是一个例子:
template<class T>//T为任意类型
inline const T& max(const T& left,const T& right)
//可以定义为内联函数,inline必须放在模板参数表之后,在返回值之后,不能放在template之前
{
//if left
return left
二、函数模板又是什么?
(1)代表了一个函数家族,该函数与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
(2)不是一个实在的函数,编译器不能为其生成可执行代码。定义函数模板后只是一个对函数功能框架的描述,当它具体执行时,将根据传递的实际参数决定其功能。
(3)模板函数的格式
template<typename T1,typename T2,......class Tn>
返回值类型 函数名 (参数列表)
{
//函数体
}
typename是定义模板参数关键字,也可以用class。建议尽量使用typename,但是不能是使用struct
三、模板参数(**函数模板有两种参数:模板参数和调用参数)
模板形参又分为:模板类型形参和非模板类型形参
模板类型形参:
模板形参名字只能在模板形参之后到模板声明或定义的末尾之间使用,遵循名字屏蔽规则
//名字屏蔽规则
typedef char T;
template<class T>
void Fun(T t)
{
cout<<"t Type="<<typeid(t).name()<int main()
{
int a=10;
Fun(a);
cout<<"g Type="<<typeid(g).name()<"pause");
return 0;
}
模板形参的名字在同一个模板形参列表中只能使用一次(不能出现下面的情况)
templateT,class T> //不能定义缺省的模板实参
T Add(T a,T b) //编译器报错:重定义模板参数"T"
{
return a+b;
}
非模板类型形参
template<typename T,int N>//定义有非模板类型参数
void FunTest(T (&array)[N])//N定义数组的长度
{
for(int iIdx = 0;iIdx < N;++iIdx)
{
array[iIdx]=0;
}
}
int main()
{
int a[10];
float b[10];
FunTest(a);
FunTest(b);
system("pause");
return 0;
}
四、函数模板的实例化:
template <class T>
T Add(T a, T b)
{
return(a + b);
}
int main()
{
cout << Add(10, 20) << endl;
cout << Add(10.2, 20.3) << endl;
system("pause");
return 0;
}
当调用一个函数模板时,编译器通常会用函数实参来为我们推断模板实参,如上面例子,当我们调用Add函数时,编译器使用实参的类型来确定绑定到模板参数T的类型,通过查看反汇编,我们看到第一个调用,编译器为我们实例化了一个int型的函数,第二个调用,实例化了double型的函数。
五、模板编译(被编译了两次)
a.实例化之前,检查模板代码本身,查看是否出现语法错误;
b.在实例化期间,检查模板代码,查看是否所有的调用都有效。
六、类型形参转换
const转换:
接受const引用或const指针的函数可以分别用非const对象的引用或者指针来调用;
template<typename T>
void Add(const T& a)//接受const引用的函数
{
cout<<typeid(a).name()<int main()
{
int iNum1=10;
int &iNum2 = iNum1;
Add(iNum2);//非const对象iNum1的引用iNum2
system("pause");
return 0;
}
template<typename T>
void Add(const T* a)//接受const指针的函数
{
cout << typeid(a).name() << endl;
}
int main()
{
int a = 10;
int *pa = &a;
Add(pa);//非const对象的指针
system("pause");
return 0;
}
数组或函数到指针的转换:
如果模板参数不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。
//类型形参转换(数组)
template<typename T>
void Add(T a)
{
cout<<typeid(a).name()<int main()
{
int a2[3];
Add(a2);
system("pause");
return 0;
}
//类型形参转换(函数)
template<typename T>
void Add(T a)
{
cout<<typeid(a).name()<//函数指针类型
}
void foo()
{
int a=20;
}
int main()
{
Add(foo);
system("pause");
return 0;
}
七、模板函数重载
template <class T>
T Add(T a, T b)
{
cout << "模板函数Add" << endl;
return(a + b);
}
//一个非模板函数可以和一个同名的函数模板同时存在,
//而且该函数模板还可以被实例化为这个非模板函数。
char Add(char a, char b)
{
cout << "非模板函数Add" << endl;
return(a + b);
}
int main()
{
Add(5, 6);
Add('1', '2');
Add<>('1', '2');
Add(1, (int)'A');
Add(1, 'A');
Add<int>('1', '2');
Add<int>(1, '2');
system("pause");
return 0;
}
具体分析:
八、模板特例化
在编写单一模板时,不可能达到使任何类型的模板实参都能实例化,在某些情况下,通用模板的定义对特定的类型式不合适的,通用定义可能编译失败,或做得不正确。
compare函数是证明上述的很好的证据,它展示了函数模板的通用定义不适合一个特定类型(即字符指针)
template<typename T>
int compare(const T& v1, const T& v2)
{
cout << typeid(v1).name() << endl;
if (v1 < v2)
return -1;
else
return 1;
return 0;
}
int main()
{
char* s1 = "hello";
char* s2 = "daidi";
cout << compare(s1, s2) << endl;
system("pause");
return 0;
}
当我们不能(或不希望)使用模板版本时,可以定义类或函数模板的一个特例化版本。
(1)特化的定义:
template后面跟一个空尖括号对<>;空尖括号指出我们将为原模板的所有模板提供实参;
具体代码:
template<typename T>
int compare(const T& v1, const T& v2)
{
cout << "非特化版本" << endl;
if (v1 < v2)
return -1;
else
return 1;
return 0;
}
//compare的特殊版本,处理字符数组的指针
template<>
int compare<const char*>(const char* const &p1, const char* const &p2)
{
cout << "特化版本" << endl;
return strcmp(p1, p2);
}
int main()
{
const char* s1 = "hello";
const char* s2 = "daidi";
cout << compare(s1, s2) << endl;
char* s3 = "hello";
char* s4 = "daidi";
cout << compare(s3, s4) << endl;
system("pause");
return 0;
}