模板通式
函数模板通式
|
|
类模板通式
|
|
模板的非类型形参
非类型模板形参:模板的非类型形参也就是内置类型形参,如template
class B{};其中int a就是非类型的模板形参。 非类型形参在模板定义的内部是常量值,也就是说非类型形参在模板的内部是常量。
非类型模板的形参只能是整型,指针和引用,像double,String, String **这样的类型是不允许的。但是double &,double *,对象的引用或指针是正确的。
调用非类型模板形参的实参必须是一个常量表达式,即他必须能在编译时计算出结果。
注意:任何局部对象,局部变量,局部对象的地址,局部变量的地址都不是一个常量表达式,都不能用作非类型模板形参的实参。全局指针类型,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参。
全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参。
sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参。
当模板的形参是整型时调用该模板时的实参必须是整型的,且在编译期间是常量,比如template
class A{};如果有int b,这时A m;将出错,因为b不是常量,如果const int b,这时A m;就是正确的,因为这时b是常量。 非类型形参一般不应用于函数模板中,比如有函数模板template
void h(T b){},若使用h(2)调用会出现无法为非类型形参a推演出参数的错误,对这种模板函数可以用显示模板实参来解决,如用h (2)这样就把非类型形参a设置为整数3。显示模板实参在后面介绍。 非类型模板形参的形参和实参间所允许的转换
- 允许从数组到指针,从函数到指针的转换。如:template
class A{}; int b[1]; A\ m;即数组到指针的转换 - const修饰符的转换。如:template
class A{}; int b; A\<&b> m; 即从int *到const int *的转换。 - 提升转换。如:template
class A{}; const short b=2; A\ m; 即从short到int的提升转换 - 整值转换。如:template
class A{}; A<3> m; 即从int 到unsigned int的转换。3> - 常规转换。
- 允许从数组到指针,从函数到指针的转换。如:template
非类型模板的应用:Stack类
stack.h
|
|
stack.cpp
|
|
模板的全特化和偏特化
所谓特化,就是将泛型的东西搞得具体化一些,从字面上来解释,就是为已有的模板参数进行一些使其特殊化的指定,使得以前不受任何约束的模板参数,或受到特定的修饰(例如const或者摇身一变成为了指针之类的东东,甚至是经过别的模板类包装之后的模板类型)或完全被指定了下来。
模板有两种特化,全特化和偏特化(局部特化)
- 模板函数只能全特化,没有偏特化(以后可能有)。
- 模板类是可以全特化和偏特化的。
全特化,就是模板中模板参数全被指定为确定的类型。全特化也就是定义了一个全新的类型,全特化的类中的函数可以与模板类不一样。
偏特化,就是模板中的模板参数没有被全部确定,需要编译器在编译时进行确定。 在类型上加上const、&、*( cosnt int、int&、int*、等等)并没有产生新的类型。只是类型被修饰了。模板在编译时,可以得到这些修饰信息。
模板为什么要特化,因为编译器认为,对于特定的类型,如果你能对某一功能更好的实现,那么就该听你的。
模板分为类模板与函数模板,特化分为全特化与偏特化。全特化就是限定死模板实现的具体类型,偏特化就是如果这个模板有多个类型,那么只限定其中的一部分。
先看类模板:
|
|
那么下面3句依次调用类模板、全特化与偏特化:
|
|
而对于函数模板,却只有全特化,不能偏特化:
|
|
至于为什么函数不能偏特化,似乎不是因为语言实现不了,而是因为偏特化的功能可以通过函数的重载完成。
函数模版的全特化不参与函数重载, 并且优先级低于函数基础模版参与匹配,也就是说,匹配的顺序是:
- 非模板函数
- 某个没有进行全特化的template function
- 如果这个没有进行全特化的template function有全特化版本,并且类型也比较匹配,则选择这个全特化版本