1.模板的背景
我们经常要比较俩个数字的大小,但是不仅有int,还有double,float,char,char *
#include <iostream>
using namespace std;
int Max(int a, int b)
{
return a>b?a:b;
}
char Max(char a, char b)
{
return a>b ? a:b;
}
float Max(float a, float b)
{
return a>b ? a:b;
}
double Max(double a, double b)
{
return a>b ? a:b;
}
int main()
{
printf("int: %d\n", Max(4,1));
printf("char: %c\n",Max('a', 'y'));
printf("float: %f\n",Max(67.6, 03.3));
printf("double: %f\n",Max(6557.6222, 0223.322));
return 0;
}
看这几个函数,同名函数,参数个数相同,类型不同;是的,这就是函数的重载;
先来看看C方式,用宏来替代;注意:定义宏一定要小心,宏就是预编译时的一种替换!
#include <iostream>
using namespace std;
#define MAX(a,b) ((a)>(b)?(a):(b))
int main()
{
printf("int: %d\n", MAX(4,1));
printf("char: %c\n",MAX('a', 'y'));
printf("float: %f\n",MAX(67.6, 03.3));
printf("double: %f\n",MAX(6557.6222, 0223.322));
return 0;
}
结果与上面一致(编译器Dev C++ 5.7.0)
结果与上面是一样的;可是如果是复杂的函数用宏行得通吗?
我们试着编一个程序:计算一个整型数组arr的元素个数。
#include <iostream>
using namespace std;
#define MIN(a,b) ((a) < (b) ? (a) : (b))
const int size = 10;
int arr[size];
int main()
{
int elem_cnt = 0;
int *p = &arr[0];
// count the number of array elements
while ( MAX(p++,&arr[size]) != &arr[size] )
{
++elem_cnt;
}
cout << "elem_cnt : " << elem_cnt
<< "\texpecting: " << size << endl;
return 0;
}
为什么不一样呢?
MIN()的宏扩展在这种情况下会失败; 是因为应用在指针实参p 上的后置递增操作随每次扩展而被应用了两次。
MIN(p++,&arr[size])
宏替换后:
(p++) < (&arr[size]) ? (p++) : (&arr[size])
>_< 再来看看今天的主角:c++方式。搞C++,就是要效率,通用;正所谓求同存异,把相同的整齐化一,把不同的请求交给user。就是说,把max函数体内的语句化一,把各个函数的类型(参数列表和返回值)参数化,让user(main()里)来确定异的是什么(参数类型);
来一段官方标准术语:函数模板提供了一种机制,通过它我们可以保留函数定义和函数调用的语义。在一个程序位置上封装了一段代码,确保在函数调用之前实参只被计算一次,而无需像宏方案那样绕过C++的强类型检查函数模板,提供一个种用来自动生成各种类型函数实例的算法。程序员对于函数接口参数和返回类型中的全部或者部分类型进行参数化parameterize 。而函数体保持不变如用一个函数的实现在一组实例上保持不变并且每个实例都处理一种惟一的数据类型。
明白了吗?
2.模板的定义
这个思想就是函数模板,我们c++有规范的模板定义方式,到底如何定义模板,使类型参数化呢?
template <class Type>
Type Max( Type a, Type b )
{
return a < b ? a : b;
}
注:将此函数代替以上二例,均可以成功。
2.1 模板的定义形式
关键字template 总是放在模板的定义与声明的最前面。关键字后面是用逗号分隔的模板参数表template parameter list, 它用尖括号<> 一个小于号和一个大于号括起来。该列表是模板参数表,
不能为空模板参数,可以是一个模板类型参数template typeparameter, 它代表了
一种类型;也可以是一个模板非类型参数template nontype parameter,它代表了一个
常量表达式。
2.1.1 模板类型参数
模板类型参数由关键字class 或typename 后加一个标识符构成。在函数的模板参数表中,这两个关键字的意义相同,它们表示后面的参数名代表一个潜在的内置或用户定义的类型模板参数名由程序员选择。在本例中我们用Type 来命名min()的模板参数但实际上可以以任何名字譬如
template <class Glorp>
Glorp min( Glorp a, Glorp b ) {
return a < b ? a : b;
}
2.1.2 模板非类型参数
模板非类型参数由一个普通的参数声明构成。模板非类型参数表示该参数名代表了 一个潜在的值。而该值代表了模板定义中的一个常量。
例如size 是一个模板非类型参数它代表arr 指向的数组的长度
template <class Type, int size>
Type min( Type (&arr) [size] );
2.2 模板的实例化
当模板被实例化时,实际的内置或用户定义类型将
替换模板的类型参数。当函数模板min()被实例化时size 的值会被一个编译时刻已知的常量值代替
?那么请问什么时候实例化呢?到底怎么一回事呢?