编写与类型无关的同用代码,是代码复用的一种手段,模板是泛型编程的基础
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本
template
返回值类型 函数名(参数列表){}
template
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
注意:typename是用来定义模板参数关键字,也可以用class
函数模板的原理是根据参数化的类型,编译器在实例化阶段生成针对具体类型的函数代码。
当我们调用一个函数模板时,编译器会根据提供的实际参数类型,自动推导出模板参数的具体类型。然后,编译器会实例化函数模板,即根据具体类型生成针对该类型的函数代码。
函数模板的编译器会根据具体的实际类型进行类型检查,以确保代码的类型安全性。如果参数类型不符合模板定义的约束条件,编译器会报错。
用不同类型的参数使用函数模板时,称为函数模板的实例化
函数参数实例化分为:隐式实例化和显示实例化
1.隐式实例化:让编译器根据实参推演模板参数的实际类型
#include
using namespace std;
template
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.1, d2 = 20.1;
cout << Add(a1, a2) << endl;
cout << Add(d1, d2) << endl;
return 0;
}
2.显示实例化:在函数名后的<>中指定模板参数的实际类型
#include
using namespace std;
template
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a = 10;
double b = 20.0;
cout<(a, b)<
如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译将会报错
1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为则这个非模板函数
#include
using namespace std;
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template
T Add(T left, T right)
{
return left + right;
}
void Test()
{
int sum1=Add(1, 2); // 与非模板函数匹配,编译器不需要特化
int sum2=Add(1, 2); // 调用编译器特化的Add版本
cout << sum1 << endl;
cout << sum2 << endl;
}
int main()
{
Test();
return 0;
}
2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个新的实例。如果模板可以产生一个具有更好匹配的函数,那么将选择模板
#include
using namespace std;
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template
T1 Add(T1 left, T2 right)
{
return left + right;
}
void Test()
{
cout << Add(1, 2) << endl;// 与非函数模板类型完全匹配,不需要函数模板实例化
cout << Add(1, 2.1) << endl; // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的Add函数
}
int main()
{
Test();
}
3.模板函数不允许类型转换,但普通函数可以进行自动类型转换。
template
class 类模板名
{
// 类内成员定义
};
// 类模板定义
template
class MyArray {
private:
T* arr;
int size;
public:
MyArray(int s) {
size = s;
arr = new T[size];
}
~MyArray() {
delete[] arr;
}
T& operator[](int index) {
return arr[index];
}
};
// 使用类模板
int main() {
MyArray intArray(5);
for (int i = 0; i < 5; i++) {
intArray[i] = i;
}
MyArray doubleArray(3);
for (int i = 0; i < 3; i++) {
doubleArray[i] = 0.5 * i;
}
return 0;
}
类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
//vector类名,vector才是类型
vector s1;
vector s2;
模板形参分类类型形参与非类型形参
类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称
非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用
#include
using namespace std;
namespace bite
{
// 定义一个模板类型的静态数组
template
class array
{
public:
T& operator[](size_t index)
{ return _array[index]; }
const T& operator[](size_t index) const
{ return _array[index]; }
size_t size() const
{ return _size; }
bool empty() const
{ return 0 == _size; }
private:
T _array[N];
size_t _size=N;
};
}
int main() {
bite::array arr; // 创建一个大小为 5 的整数数组
// 对数组赋值
for (int i = 0; i < arr.size(); i++) {
arr[i] = i + 1;
}
// 输出数组元素
for (int i = 0; i < arr.size(); i++) {
cout << arr[i] << " ";
}
return 0;
}
【注意】
- 浮点数、类对象以及字符串是不允许作为非类型模板参数的
- 非类型的模板参数必须在编译期就能确认结果
使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要进行特殊处理。模板的特化分为函数模板特化与类模板特化
函数模板的特化步骤:
// 函数模板 -- 参数匹配
template
bool Less(T left, T right)
{
return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less(Date* left, Date* right)
{
return *left < *right;
}
全特化即是将模板参数列表中所有的参数都确定化
template
class Data
{
public:
Data() {cout<<"Data" <
class Data
{
public:
Data() {cout<<"Data" < d1;
Data d2;
}
任何针对模板参数进一步进行条件限制设计的特化版本
template
class Data
{
public:
Data() {cout<<"Data" <
部分特化
将模板参数类表中的一部分参数特化
// 将第二个参数特化为int
template
class Data
{
public:
Data() {cout<<"Data" <
参数更进一步的限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本
//两个参数偏特化为指针类型
template
class Data
{
public:
Data() {cout<<"Data" <
class Data
{
public:
Data(const T1& d1, const T2& d2)
: _d1(d1)
, _d2(d2)
{
cout<<"Data" < d1; // 调用特化的int版本
Data d2; // 调用基础的模板
Data d3; // 调用特化的指针版本
Data d4(1, 2); // 调用特化的引用版本
}
一个程序由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式
将声明和定义放到一个文件 “xxx.cpp” 里面或者“xxx.h”其实也是可以的。
//test.h
#pragma once
#include
#include
using namespace std;
template
T Add(const T& left, const T& right)
{
return left + right;
}
//test.cpp
#include"test.h"
int main()
{
cout << Add(1, 2) << endl;
cout << Add(1.0, 2.0) << endl;
return 0;
}