模板分为 函数模板 和 类模版
泛型编程:编写与类型无关的逻辑代码,是代码复用的一种手段。模板是泛型编程的基础。
函数模板
函数模板:代表了一个函数家族,该函数与类型无关,在使用时被参数化,根据实参类型产生函数的
特定类型版本。
模板函数的格式
template<typename Param1, typename Param2,...,class Paramn>
返回值类型 函数名(参数列表)
{
...
}
// typename和class都可以 但struct不行
模板函数也可以定义为inline函数,inline关键字必须放在模板形参表之后,返回值之前,不能放在template之前
模板是一个蓝图,它本身不是类或者函数,编译器用模板产生指定的类或者函数的特定类型版本,产
生模板特定类型的过程称为函数模板实例化。
#include <iostream>
using namespace std;
template <typename T, class T1> // 模板形参名可以任意给
inline T Add(T left, T1 right)
{
return left + right;
}
int main()
{
cout <<Add('1', 1) << endl; //相当调用 char Add(char left, int right)
cout << Add<int, char>('1', 1) << endl; //相当调用 int Add(int left, char 1)
system("pause");
return 0;
}
注意:在函数模板的内部不能指定缺省的模板实参。模板形参的名字在同一模板形参列表中只能使用一次.
模板被编译了两次:
实例化之前,检查模板代码本身,查看是否出现语法错误,如:遗漏分号
在实例化期间,检查模板代码,查看是否所有的调用都有效,如:实例化类型不支持某些函数调
用
一般不会转换实参以匹配已有的实例化,相反会产生新的实例。
编译器只会执行两种转换:
1、const转换:接收const引用或者const指针的函数可以分别用非const对象的引用或者指针来调用
2、数组或函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指
针转换。数组实参将当做指向其第一个元素的指针,函数实参当做指向函数类型的指针。
#include <iostream>
using namespace std;
template <typename T>
T Add( const T left, T right)
{
return left + right;
}
template <typename T>
void display( T t)
{
cout << *t << endl;
}
int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
int a = 10;
const int& ra = a;
cout << Add(ra, 1) << endl; //const转换
display(arr); //数组的转换
system("pause");
return 0;
}
函数模板有两种类型参数:模板参数和调用参数
模板形参名字只能在模板形参之后到模板声明或定义的末尾之间使用,遵循名字屏蔽规则
#include <iostream>
using namespace std;
typedef int T;
template <class T>
T Add(T left, T right)
{
cout << "left Type = " << typeid(left).name() << endl; //typeid(left).name() 打印类型名
return left + right;
}
T gloab;
int main()
{
cout << Add(1, 2) << endl;
cout << "gloab Type = " << typeid(gloab).name() << endl;
system("pause");
return 0;
}
非模板类型形参是模板内部定义的常量,在需要常量表达式的时候,可以使用非模板类型参数。
#include <iostream>
using namespace std;
template <class T , size_t N>
void display(T (&arr)[N])
{
for (size_t i = 0; i < N; ++i)
{
cout << arr[i] << endl;
}
}
int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
display(arr);
system("pause");
return 0;
}
模板形参说明
1、模板形参表使用<>括起来
2、和函数参数表一样,跟多个参数时必须用逗号隔开,类型可以相同也可以不相同
3、定义模板函数时模板形参表不能为空
4、模板形参可以是类型形参,也可以是非类型新参,类型形参跟在class和typename后
5、模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型或自定义类型
使用方法完全相同,可用于指定函数形参类型、返回值、局部变量和强制类型转换
6、模板形参表中,class和typename具有相同的含义,可以互换,使用typename更加直观。
但关键字typename是作为C++标准加入到C++中的,旧的编译器可能不支持。
模板函数重载
1、一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例
化为这个非模板函数。
2、对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调动非模板
函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数,
那么将选择模板。
3、显式指定一个空的模板实参列表,该语法告诉编译器只有模板才能来匹配这个调用,
而且所有的模板参数都应该根据实参演绎出来。
4、模板函数不允许自动类型转换,但普通函数可以进行自动类型转换。
#include <iostream>
using namespace std;
template <class T>
T Max(const T& left, const T& right)
{
cout << "Max(T)" << endl;
return (left > right) ? left : right;
}
int Max(const int& left, const int& right)
{
cout << "Max(int)" << endl;
return (left > right) ? left : right;
}
int main()
{
cout << Max<int>(2, 1) << endl;
cout << Max(2, 1) << endl;
cout << Max<>(2, 1) << endl;
cout << Max(2, '1') << endl;
system("pause");
return 0;
}
模板函数特化
在某些情况下,通用模板定义也木有那么强大。
#include <iostream>
using namespace std;
template <class T>
int compare(const T s1, const T s2)
{
if (s1 > s2)
return 1;
else if (s1 < s2)
return -1;
else
return 0;
}
int main()
{
char* s1 = "dddddd";
char* s2 = "bbbbbb";
cout << compare(s1, s2) << endl;
system("pause");
return 0;
}
实际上他们比较的是地址
那么就需要模板函数特化
#include <iostream>
using namespace std;
template <class T>
int compare(const T s1, const T s2)
{
if (s1 > s2)
return 1;
else if (s1 < s2)
return -1;
else
return 0;
}
template<>
int compare<char*>(char* s1,char* s2)
{
return strcmp(s1, s2);
}
int main()
{
char* s1 = "dddddd";
char* s2 = "bbbbbb";
cout << compare(s1, s2) << endl;
system("pause");
return 0;
}
注意:在模板特化版本的调用中,实参类型必须与特化版本函数的形参类型完全匹配,
如果不匹配,编译器将为实参模板定义中实例化一个实例。
类模板
模板类也是模板,必须以关键字template开头,后接模板形参表。
template<class 形参名1, class 形参名2, ...class 形参名n>
class 类名
{ ... };
类模板模拟实现Vector:http://blog.csdn.net/l_listen/article/details/78373516
类模板模拟实现List:http://blog.csdn.net/l_listen/article/details/78373654