一、函数模板
1.1 函数模板概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
//typename是用来定义模板参数关键字,也可以使用class
//但是不能使用struct代替class
template<typename T>
//tempalte<class T>
void Swap(T& a, T& b)
{
T c = a;
a = b;
b = c;
}
int main()
{
int a = 10;
int b = 20;
Swap(a, b);//编译器需要根据传入的实参类型,来推演生成对应类型的函数以供调用
return 0;
}
1.2 函数模板实例化
1.2.1隐式实例化
//隐式实例化:让编译器根据实参推演模板参数的实际类型
template<typename T>
T Add(const T& a, const T& b)
{
return (a + b);
}
int main()
{
int a = 1, b = 2;
double c = 1.1, d = 2.2;
Add(a, b);//int
Add(c, d);//double
//Add(a, d);
//a将T推演成int,d将T推演成double
//编译器无法确定此处到底该将T确定为int 或者 double类型而报错
//解决方法:
//1.自己强制转化
Add(a, (int)d);
Add((double)a, d);
//2.显示实例化
return 0;
}
1.2.2显示实例化
//显示实例化:在函数名后的<>中指定模板参数的实际类型
template<typename T>
T Add(const T& a, const T& b)
{
return (a + b);
}
int main()
{
int a = 1, b = 2;
double c = 1.1, d = 2.2;
Add(a, b);//int
Add(c, d);//double
//Add(a, d);
//.显示实例化
Add<int>(a, d);//会将d强制类型转换为int
Add<double>(a, d);//会将a强制类型转换为double
return 0;
}
1.3 模板参数的匹配原则
1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这
个非模板函数
int Add(int a, int b)
{
return a + b;
}
template<class T>
T Add(T a, T b)
{
retrurn a + b;
}
int main()
{
int a = 1, b = 2;
Add(a, b);//调用第一个
Add<int>(a, b);//调用编译器特化的函数
return 0;
}
2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而
不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模
板
int Add(int a, int b)
{
return a + b;
}
template<class T,class N>
T Add(T a, N b)
{
retrurn a + b;
}
int main()
{
Add(1,2);//与非函数模板类型完全匹配,调用第一个
Add(1,2.2);//编译器根据实参,函数模板可生成更匹配的,调用函数模板
return 0;
}
二、类模板
2.1 类模板的定义格式
template <class T>
class Stack
{
public:
Stack(T capacity = 1)
:a(new T[capacity])
,capacity(_capacity)
,size(0)
{}
~Stack()
private:
T* a;
T _capacity;
T _size;
};
//类模板中函数放在类外进行定义时,需要加模板参数列表
template<class T>
Stack<T>::~Stack()
{
if (a)
dalete[] a;
_capacity = _size = 0;
}
2.2 类模板的实例化
类模板实例化需要在类模板名字后跟 <> ,然后将实例化的 类型放在 <> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类 。
//Stack类名,Stack<int>才是类型
Stack<int> S;
Stack<double T;
三、模板的分离编译
模板不支持声明和定义分开放(xxx.h xxx.cpp)
一般将声明和定义放到一个文件(命名为xxx.hpp)中