1.泛型编程:(1)与类型无关 (2)通用 (3)任意场景都可处理
2.模板:是泛型编程的基础。与类型无关
3.函数模板:
(1)该函数与类型无关,在使用时被参数化,根据参数类型产生函数的特定类型版本。
template <typename T>
T Add(T a, T b)
{
return a + b;
}
int main()
{
Add(1, 2);
Add(1.0, 2.0);
Add('1', '2');
return 0;
}
此时就写了一个函数模板,与类型无关。
这里的typename也可写出class, T就是类型。
注意:这里可以用class但是不可以用struct。也就是说,模板参数列表只能用class。
(2)原理:
模板并不是函数,是编译器生成代码的规则
转到汇编代码看到,这里调用的函数是:Add<int>...,
所以说模板并不是函数,而是通过模板来推演出我们所需类型的函数。
假如参数类型不一致,编译器不会做类型转化。(自己可以进行隐式转化)
(3)参数模板的实例化:
模板参数的实例化分为两种:隐式转化和显示实例化
隐式实例化:编译器自动识别参数类型并推演成一个实例
Add<int>(1, 2);
显示实例化: 直接命令编译器创建指定类型的函数实例。
Add<int>(1, 2);
但是内部有可能做隐式转化
Add<int>(1, '2')
(4)函数模板参数的匹配规则
1)一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
2)对于非模板函数和同名函数模板,若其他条件相同,在调用的时候会优先调用非模板函数而不会从该模板产生一个实例。如果模板可以产生一个具有更好匹配的函数,那么选择模板。
int Add(int a, int b)
{
return a + b;
}
template <typename T1,typename T2>
T1 Add(T1 a, T2 b)
{
return a + b;
}
int main()
{
Add(1, 2);
Add(1, '2');
Add(1.0, 2.0);
Add('1', '2');
return 0;
}
对于 1)为什么两个可以同时存在,是因为两个函数并不会冲突。一个函数名是Add ,一个函数名是Add<>
对于2) 此时第一个Add就会调用自己写的函数。 对于第二个Add就会去调用自己写的,虽然调自己的可以做类型提升,但是调用模板的更合适.
3)显示指定一个空的模板实参列表,该语法告诉编译器只有模板才能来匹配这个调用,而且所有的模板参数都应该根据实参来演绎出来
Add<>(1, 2);
4)模板函数不许与自动的类型转换,但是普通函数可以进行自动类型转换。
Add(1, '2');
只有在显示实例化的时候,模板才可能在内部做类型转换
问题:下面比较的模板可以比较字符串吗?
template<class T>
T Max(const T& left, const T& right)
{
return (left > right) ? left : right;
}
int main()
{
char *a = "ebc";
char *b = "cba";
cout << Max(a, b) << endl;
return 0;
}
发现结果是错的。此时他的比较规则是地址。所以对于自定义类型模板可能不可处理,将其方法进行重写写一下。
const char* Max(const char* left, const char* right)
{
if (strcmp(left, right) > 0)
return left;
return right;
}