函数模版:
代表了一个函数家族,该函数与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
模版是一个蓝图,本身不是类或者函数,编译器用模版产生指定的类或者函数的特定类型版本,产生模版特定类型的过程成为模版的实例化。
注意:模版被编译两次
①函数实例化之前,检查模版代码本身,查看是否有语法错误;
②实例化期间,检查模版代码,查看是否所有的调用都有效,
如:实例化类型不支持某些函数调用。
实参推演
从函数实参确定模版形参类型和值的过程称为:模版实参测推演
类型形参转换:
一般不会转换实参以匹配已有的实例化,相反会产生新的实例。
编译器只执行两种转换:
①const转换:接收const引用或者const指针的函数可以分别用非const对象的引用或者指针来调用
②数组和函数的转换:1、数组实参将转换为指向其第一个元素的指针 2、函数实参将转换为指向函数类型的指针。
模版参数:
函数模版有两种类型的参数:模版参数和调用参数
模版形参:①:模版类型形参 ②:非模版类型形参
模版形参名字只能在 模版形参之后 到 模版声明或定义的末尾之间,遵循名字屏蔽规则
代码中: t 是模版函数实例化的形参,因为传进去的是10.0所以出来的是double类型
_g是 类型重新定义的T(int )定义的全局变量,所以输出的是int
此结果验证了:模版形参的名字只在形参之后到模版声明或定义的末尾之间使用。
模版形参的名字 在同一个模版形参列表中只能使用一次
所有的模版形参前面必须加上typename 或者 class 关键字的修饰
【非模版类型参数】
非模版类型参数是模版内部定义的常量,在需要常量表达式的时候,就可以使用非模版类型参数
Eg:数组长度
总结:模版形参的说明
1、模版形参表使用<>括起来;
2、和函数的参数表一样,多个参数必须逗号隔开,类型可以相同或者不同。
3、定义你的模版函数时候,模版形参表是不能为空的。
4、模版形参表可以是类型形参,也可以是非类型形参,类型形参必须在class 或者typename 后面。
5、模版形参可以作为类型的说明符,在模版中的任何一个地方使用,与内置类型、自定义类型的用法完全相同。可以用来指定函数的返回类型,返回值,局部变量和强制转换。
6、模版形参表中,class和typename 具有相同的含义,可以互换,使用typename更加的直观,旧一点的编译器可能不支持typename。
模版函数的重载:
注意:函数的所有重载的版本的声明都应该位于该函数被调用之前。
【说明】
1、一个非模版函数可以和一个同名的函数模版同时存在,而且该函数模版还是可以被实例化为这个非函数模版。
2、对于非模版函数和同名的函数模版,如果其他条件相同,在调用时会首先调用非模版函数而不会从函数模版里面产生一个实例。如果函数模版可以产生一个具有更好匹配的函数,那就调用函数模版产生新的函数。
3、显式指定一个空的模版实参列表,该语法告诉编译器只有模版才能来匹配这个调用,而且所有的模版参数都应该根据实参演绎出来。
4、模版函数不允许自动类型转换,普通函数可以进行自动类型转换。
【模版函数的特化】
在某些情况下,通用模版定义对于某个类型可能是完全错误的,或者不能编译,或者是做一些错误的事情。
比如:
此时会出来一个错误的结果。
所以改正方法:
此时在模版特化版本的调用中,实参类型必须与特化版本函数的形参的类型必须完全匹配,
如果不匹配,编译器就会在用函数模版是实例化一个char*的实例,去供使用。
【模版类】
//以模版的方式实现动态顺序表
当定义上述两种类型的顺序表的时候,编译器会使用两中不同的类型分别代替模版里面的模版类型,从而形成不同SeqList类。
即:SeqList<int>类 和SeqList<double>类
【模版参数------实现容器适配器】
模版的模版参数------容器适配器
【非类型的类模版参数】
//静态顺序表
注意:浮点数和类对象是不允许作为非类型模版参数的
【类模版的特化】
1、全特化
【偏特化(局部特化)】
局部特化第二个变量,即:<T1,int>
局部特化两个形参为指针类型,即:<int*,int*>
局部特化两个形参是引用类型,即:<T1&,T2&>
模版的总结:
优点:模版复用了代码,节省了资源,更快的迭代开发。C++的变准模板库(STL)由此产生。增强了代码的灵活性。
缺点:模版让代码变得凌乱复杂,不易维护,编译代码时间变长。
出现模版编译错误时,错误信息非常凌乱,不易定位错误。