C++标准库的开发,几乎所有东西都被设计为模板形式,所以掌握Template技术是学习标准库的基础。
回顾下Template,它是一种针对“一个或多个尚未明确类型”所编写的函数或类别,其中共性是逻辑相同,异处在于该逻辑中使用到的类型需要用户自己设定。
当然,我们在使用Template时,可以“显式explicitly”或“隐式implicitly”地将类型当做参数来传递。
最简单的:
template <class T> // 因为class尚未确定,暂时用T代替 inline const T& max (const T& a, const T& b) { return a < b? b : a; }
Template并非一次编译就能够产生出所有可用类型的代码,而是需要用户进行设定,设定完之后编译器才能根据用户设定的值进行产生合适的类型,来产生调用操作。
关于Template函数可移植性的操作方式就是在头文件中以inline function来实现。
接下来需要对template的几个重要特性进行说明:
-----------------------------------------------------定义函数模板-----------------------------------------------------
1. 模板形参表:
定义的这些形参就像函数形参表,该形参表定义了特定类型的局部变量,但并不初始化,直到运行时再提供实参来初始化形参。
模板形参可以是:表示类型的类型形参(type)、表示常量表达式的非类型形参(non-type)
类型形参的关键字是class或typename,其实两个关键字没什么大区别。
2. 使用函数模板:
使用函数模板时,编译器会推断哪个模板实参绑定到模板形参,一旦编译器确定了实际的模板实参,那么这个编译器就实例化了函数模板的一个实例。
所以,是由编译器来承担为我们使用的每种类型而编写函数的单调工作。
3. inline函数模板:
函数模板加上inline的方式和一般函数类似,但写的时候注意,是放在模板形参之后,函数返回类型之前:
template <typename T>
inline T play(const T& t1, const T& t2);
-----------------------------------------------------定义类模板-----------------------------------------------------
1. 使用类模板:
必须为模板形参显式的指定实参:
Queue<int> qi;
Queue<vector<double>> qc;
Queue<string> qs;
实例化工作还是由编译器来完成。
模板形参:
就好像一般的函数形参,形参名字为任意设计,名字前需要添加“类型”。
区别在于,形参可以是类型形参(一个未知类型),也可以是非类型形参(一个未知值)。
模板类型形参:
由关键字class或typename构成,这两个关键字作用基本相同,只是typename比class更直观。
对于模板内部定义类型成员,但编译器无法知道这个成员是一个值还是类型:
template <typename T> void Function(T* t) { T::size_type *p; // 这个size_type是自定义的,所以编译器默认是收成员,而不是类型成员 // 除非 typename T::size_type *pi; }
所以,typename还是给类型成员指定是类型还是数据。
非类型模板形参:
假如模板的类型不是一个类型,而是一个数据,该形参是用来作为模板内部的常量值。
那么我们如何来编写一个泛型程序呢?这取决于某段代码是否是类型不确定的。比如一个比较操作:
if (v1 < v2) return -1;
if (v1 > v2) return 1;
return 0;
那么v1与v2有很多种可能,常量或类型?? 如果是常量则比较简单,但如果是类型,是否重载了<与>运算符的操作呢?
模板实例化:
模板如果看成是图纸,那么实例化就是按照图纸制作出实际产品,这需要用户提供模板实参,由编译器来完成实例化过程。
1. 实例化类时,编译器首先会自动创建用户指定名字的类。实例化时,指定模板实参是必须的。
2. 但实例化函数模板时,根据用户填入的实参,编译器可能会进行自动判断实参类型。
成员模板:
一个类的成员函数也可以是模板函数,但不可以是虚函数,也不能有缺省参数:
class MyClass { // .... template <class T> void fun(const T &t); }