概述:
1、所谓泛型编程,是以独立于任何特定类型的方式编写代码。使用泛型编程时,我们需要提供具体程序实例所操作的类习惯或者值。
2、模板是泛型编程的基础。模板是创建类型或者函数的蓝图或者公式。我们给这些蓝图或者公式提供足够的信息,让这些蓝图或者公式真正变成具体的类或者函数,这种转变发生在编译时。
3、模板支持将类型作为参数程序设计方式,从而实现了对泛型程序设计的直接支持。也就是说C++模板机制语序在定义类、函数时将类型作为参数。
模板一般分为 函数模板与类模板
函数模板的定义
template<typename T>
T funcadd(T a, T b)
{
T he = a + b;
return he;
}
1、模板定义使用template关键开头的,后面跟<>,<>里边 叫 模板参数列表(模板实参), 如果模板参数列表中有多个参数,则用逗号分开。<>里必须至少有一个模板参数。模板参数前面有一个关键字 typename/class(不是用来定义类的)
2、如果模板参数列表中有多个模板参数,那你就要用多个typename/class:<typename T, typename Q>
3、模板参数列表中表示在函数定义中要用到的 “类型” 或者 “值”,也和函数的参数列表类似。我们用的时候,有的时候得指定模板实参给他,指定的时候我们要用<>把模板实参包起来。有的时候又不需要我们指定模板实参,系统能够根据一些信息推断出来。
4、funcadd这个函数声明了一个名字为T的类型参数,这里T实际是类型,这个T到底是啥类型,编译器在编译的时候会根据funcadd的调用来确定。
函数模板的使用
函数模板调用函数调用区别不大,调用的时候,编译器会根据你调用这个函数模板时的实参去推断这个模板参数列表里的参数类型。
模板参数是推断出来的:推断的依据是根据你调用这个函数的时候(有的时候需要你提供),函数的实参来推断的。当然有时候光凭借你提供的函数实参推断不出来模板参数,这个时候就需要用<>来主动提供模板参数了。
int he = funcadd(2, 1); //2,1系统认为是int,所以系统能推断出模板的形参是int,也就是
//说T是int型
int he = funcadd(2.1f, 1); //报错,无法推断是double型还是int型
int he = funcadd(2.1f, 1.1f); //可以,这里返回仍为double型
编译器在推断出来这个模板的形参类型之后,编译器就会为我们实例化一个特定版本的函数。
非类型模板参数
因为T前边有一个typename/calss ,这表示T代表一个类型,是一个类型参数。
那么在模板参数列表里边,还可以定义非类型参数;非类型参数代表的是一个值。
既然非类型参数代表一个值,那么我们肯定不能用typename/class这种关键字来修饰这个值。
我们当然要用以往学习过的传统类型名来指定费类型参数。比如你非类型参数S,如果是个整型,那么就用 int s。
当模板被实例化时,这种非类型模板参数的值,或者是用户提供的,或者是编译器推断的,都有可能。
但是这些值都必须是常量表达式。因为实例化这些模板实在编译器编译时进行的。
template <int a, int b>
int func2()
{
int he = a + b;
return he;
}
int result = func2<2, 3>(); //显示指定模板参数——用<>提供额外信息
int i = 12;
int result2 = func2<i, 3>(); //不可以,报错,必须给定编译时就能确定的值这里i是在运行
//时才能确定的值
实例化模板实在编译时进行的不是运行时
template <typename T, int a, int b>
int func3(T c)
{
int result = (int)c + a + b;
return result;
}
int i = func3<int, 11, 12>(13);
int i = func3<double, 11, 12>(13); //这里系统会以<>传递的类型为准,将13转成double型
这里 L1与L2的值是编译器推断出来的
template <unsigned L1, unsigned L2>
int charscomp(const char (&p1)[L1], const char (&p2)[L2])
{
return strcmp(p1, p2);
}
int result3 = charscomp("test1","test"); //没有提供费类型模板参数,系统会根据test1
//的长度6个,test的长度5个,来取代L1,L2
模板函数可以是内联函数
template <typename T, int a, int b>
inline
int func3(T c)
{
int result = (int)c + a + b;
return result;
}
模板定义并不会导致编译器生成代码,只有在我们调用这个函数模板时,使用编译器为我们实例化了一个特定版本的函数之后编译器才会生成代码。
编译器生成代码的时候,需要能够找到函数的函数体,所以,函数模板的定义通常都是在 .h 中