在C++中,函数模板是一种参数化的工具,可以用来创建一个通用的函数,可以处理多种不同的数据类型。
泛型编程是通过编写与类型无关的通用代码,来实现代码复用。因此,C++设置了模板以实现编写与类型无关的通用代码,从而实现代码复用。
在C++中,模板分为函数模板和类模板两种。模板通过使用 template 关键字声明
函数模板
函数模板与类型无关,只有在使用时才会根据实参类型产生特定的函数。
函数模板的格式为:
template<typename T1, typename T2, ..., typename Tn>
返回类型 函数名(参数列表)
{
// 函数体
}
//或者也可以用class
template<class T1, class T2, ..., class Tn>
返回类型 函数名(参数列表)
{
// 函数体
}
具体代码演示:
template<class T>
void Swap(T& p1, T& p2)
{
T tmp = p1;
p1 = p2;
p2 = tmp;
}
参数模板的实例化
与类的实例化类似,用不同类型的参数使用函数模板就成为函数模板的实例化。在函数模板的实例化中,又分为隐式实例化和显示实例化。
隐式实例化又成为推导实例化,即让编译器根据实参自动推导具体类型。
显示实例化即要手动指定具体类型。
代码示例:
#include<iostream>
using namespace std;
template<class T>
void Swap(T& p1, T& p2)
{
T tmp = p1;
p1 = p2;
p2 = tmp;
}
int main()
{
int a = 11, b = 22;
double m = 3.6, n = 9.6;
//隐式实例化
cout << "a=" << a << " b=" << b << endl;
Swap(a, b);
cout << "a=" << a << " b = " << b << endl;
cout << "m=" << m << " n=" << n << endl;
Swap(m, n);
cout << "m=" << m << " n=" << n << endl;
return 0;
}
#include<iostream>
using namespace std;
template<class T>
T Add(const T& p1, const T& p2)
{
return p1 + p2;
}
int main()
{
int a1 = 10, a2 = 20;
double b1 = 3.6, b2 = 8.8;
//隐式实例化
cout << Add(a1, a2) << endl;
cout << Add(b1, b2) << endl;
return 0;
}
如果实参类型多样,那编译器不能自动识别出类型。
所以就需要手动指定(也就是显示实例化)。显式实例化需要在模板名字后跟<>,然后将实例化的类型放在<>中即可。
#include<iostream>
using namespace std;
template<class T>
T Add(const T& p1, const T& p2)
{
return p1 + p2;
}
int main()
{
int a1 = 10, a2 = 20;
double b1 = 3.6, b2 = 8.8;
//隐式实例化
cout << Add(a1, a2) << endl;
cout << Add(b1, b2) << endl;
//显示实例化
cout << Add<int>(a1, b1) << endl;
cout << Add<double>(a1, b1) << endl;
return 0;
}
这个函数还可以直接进行类型强转。
#include<iostream>
using namespace std;
template<class T>
T Add(const T& p1, const T& p2)
{
return p1 + p2;
}
int main()
{
int a1 = 10, a2 = 20;
double b1 = 3.6, b2 = 8.8;
//隐式实例化
cout << Add(a1, a2) << endl;
cout << Add(b1, b2) << endl;
//类型强转
cout << Add(a1, (int)b1) << endl;
cout << Add((double)a1, b1) << endl;
return 0;
}
参数模板的匹配规则
当一个非模板的函数和一个同名的函数模板同时存在时,如果其他条件都相同,编译器会优先调用
非函数模板,不会再从模板中实例化一个出来。但如果模板能产生更匹配的函数,则会使用模板。
并且模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
#include<iostream>
using namespace std;
template<class T1, class T2>
T1 Add(const T1& p1, const T2& p2)
{
cout << "模板" << endl;
return p1 + p2;
}
int Add(int p1, int p2)
{
cout << "非模板" << endl;
return p1 + p2;
}
int main()
{
Add(1, 2);
Add(1, 8.8);
return 0;
}
类模板
类模板的格式为:
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的 类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
#include<iostream>
using namespace std;
template<class T>
class Stack
{
public:
Stack(int n = 4)
:arr(new T[n])
,size(0)
,capacity(n)
{
}
void Push(const T& x)
{
if (capacity == size)
{
T* tmp = new T[capacity * 2];
memcpy(tmp, arr, sizeof(T) * size);
delete[] arr;
arr = tmp;
capacity *= 2;
}
arr[size++] = x;
}
~Stack()
{
delete[] arr;
arr = nullptr;
capacity = size = 0;
}
private:
T* arr;
int size;
int capacity;
};
int main()
{
//类模板只能显式实例化
Stack<int> st;
st.Push(1);
st.Push(2);
st.Push(3);
st.Push(4);
return 0;
}
在模板类中要想把类中的函数写到类外,就不只是指定类域那么简单。
因为模板类不是真正的类,要写到类外,就要重新写类模板,并在类模板名后加上<>,并在其中写入类类型。
#include<iostream>
using namespace std;
template<class T>
class Stack
{
public:
Stack(int n = 4)
:arr(new T[n])
,size(0)
,capacity(n)
{
}
void Push(const T& x);
~Stack()
{
delete[] arr;
arr = nullptr;
capacity = size = 0;
}
private:
T* arr;
int size;
int capacity;
};
template<class T>
void Stack<T>::Push(const T& x)
{
if (capacity == size)
{
T* tmp = new T[capacity * 2];
memcpy(tmp, arr, sizeof(T) * size);
delete[] arr;
arr = tmp;
capacity *= 2;
}
arr[size++] = x;
}
int main()
{
//类模板只能显式实例化
Stack<int> st;
st.Push(1);
st.Push(2);
st.Push(3);
st.Push(4);
return 0;
}
并且,类模版不建议声明和定义分离到两个文件(.h 和.cpp),会出现链接错误。
小结
因为swap函数会经常使用,所以C++直接提供了swap这个函数模板,可供我们使用。
#include<iostream>
using namespace std;
int main()
{
int a = 10, b = 20;
swap(a, b);
cout << a << ' ' << b << endl;
return 0;
}