泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。
如何实现一个通用的交换函数:
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
double temp = left;
left = right;
right = temp;
}
void Swap(char& left, char& right)
{
char temp = left;
left = right;
right = temp;
}
以上方法是利用了C++中支持的函数重载新特性来实现的,但是缺点在于:
在C++中,存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同材料的铸件
(即生成具体类型的代码) 因此我们又可以引申出泛型编程的基础:模板
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定
类型版本。
template
后接函数体定义。(typename是用来定义模板参数关键字,也可以使用class)
#include
using namespace std;
// 函数模板和同名函数可以同时存在
//
// 1.有现成,用现成的
// 2.有现成,但是不够匹配,有模板,选择实例化模板
// 3.有匹配也有模板,但是显式实例化模板(强制使用模板)
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
double temp = left;
left = right;
right = temp;
}
void Swap(char& left, char& right)
{
char temp = left;
left = right;
right = temp;
}
// 函数模板使用:告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码
// typename是用来定义模板参数关键字,也可以使用class
//template
template
void Swap(T& s1, T& s2)
{
T tmp;
tmp = s1;
s1 = s2;
s2 = tmp;
}
// 隐式实例化:让编译器根据实参推演模板参数的实际类型
// 显式实例化:在函数名后的<>中指定模板参数的实际类型
int main()
{
int a, b;
a = 1;
b = 0;
char str1, str2;
str1 = 'a';
str2 = 'b';
cout << "交换前:" << endl;
cout << "a=" << a << " b=" << b << endl;
cout << "str1=" << str1 << " str2=" << str2 << endl;
swap(a, b);
swap(str1, str2);
cout << "交换后:" << endl;
cout << "a=" << a << " b=" << b << endl;
cout << "str1=" << str1 << " str2=" << str2 << endl;
return 0;
}
注意:函数模板和同名函数可以同时存在:调用时候遵循以下规则:
函数模板是一个蓝图,它本身并不是函数,是编译器根据使用方式产生特定具体类型函数的模具。所以模板就是将本来应该我们做的重复的事情(写重复相似的代码)交给了编译器来做
隐式实例化:让编译器根据实参推演模板参数的实际类型
template
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.0, d2 = 20.0;
// ok!
Add(a1, a2);
Add(d1, d2);
// ok?
//Add(a1, d1);
/*
该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T,
编译器无法确定此处到底该将T确定为int 或者 double类型而报错
注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅
*/
// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化
Add(a, (int)d);//强转
Add(a, b);//显式实例化
return 0;
}
显式实例化:在函数名后的<>中指定模板参数的实际类型
int main(void)
{
int a = 10;
double b = 20.0;
// 显式实例化
Add(a, b);
return 0;
}
如果类型不匹配,编译器会尝试进行隐式类型转换,如果无法转换成功编译器将会报错