1、 在类模板(如temlpate <typename Type> class Queue;)的自身的成员函数和数据成员的定义中,如果使用类模板的名字可以省略模板参数表(<typename Type>),直接使用Queue作为类型名。但Queue在其他模板定义、类定义和函数定义中被用作一个类型名时,则必须指定完整的模板参数表。
2、 关于模板参数:
1、 绑定给模板非类类型参数的表达式必须是一个常量表达式(即其必须能在编译时被计算出结果),如const对象值,名字空间域对象的地址,sizeof表达式的结果。
2、 在模板实参的参数类型之间允许一些转换:左值转换(左值到右值,数组到指针,函数到指针),限定修饰转换(如从int *到 const int *的转换),提升,整值转换(从int到unsigned int)。
3、 模板参数不能同名,且每个参数必须都有typename或class修饰,可以有缺省实参。
3、 关于模板实例化:
1、 声明一个类模板实例的指针或引用不会引起类模板的实例化,只有当代码中检查这个指针或引用所指向的那个对象或使用了类模板的一个实例的名字,且上下文环境要求必须存在类的定义时,这个类模板才会被实例化。
2、 当类模板被实例化时,类模板的成员函数并不自动的被实例化,只有当一个成员函数被程序用到(函数调用或取地址)时,它才被实例化。
3、 类模板及其成员函数和静态数据成员的实例化点总是在名字空间域中(不在局部域和类域中),而且类模板的实例化点“总是在引用类模板实例的声明或定义”之前,其成员函数和静态数据成员的实例化点则总是跟在“引用类模板成员实例的声明或定义”之后。
4、 类模板中的名字解析分两步:
1、 在模板被定义时,解析出定义中不依赖于模板参数的名字。
2、 在模板被实例化时,解析出依赖于模板参数的名字。注意当类模板被实例化时,类模板的成员函数并不自动被实例化,只有当一个成员函数被程序用到(函数调用或取地址)时,该类模板的成员函数才被实例化。
5、 类模板中可以出现的三种友元声明:
1、 非模板友元类和友元函数,它们在被声明为友元之前,不必在全局域中声明或定义,但由于一个类成员只能由该类的定义引入,故将某个类的成员函数声明为友元之前,该类必须已经被定义。
2、 绑定的友元类模板和函数模板,在一个模板可以被用在一个类模板的友元声明中之前,它的声明或定义必须现被给出。这种方式将建立类模板实例和其友元之间的一一映射。
3、 非绑定的友元类模板和函数模板,在一个模板可以被用在一个类模板的友元声明中之前,它的声明或定义必须现被给出。这种方式将建立在类模板的实例也其友元之间定义了一对多的映射。
6、 使用类模板的嵌套模板类,将嵌套模板类的成员全部声明为public放在外围类的private数据区,则免去了将两个模板类分开定义而造成要声明友元的问题,且注意外围类模板被实例化时,其嵌套类模板不会自动被实例化,只有当上下文环境却是需要嵌套类的完整类类型时,嵌套类才会被实例化。
7、 类模板的两种编译模式(与函数模板的编译模式比较):
1、 包含编译模式,类模板的内联成员函数,非内联成员函数,静态成员都必须放在该类模板定义的头文件中。
2、 分离编译模式,类模板定义及其内联成员函数放在头文件中,而非内联成员函数和静态数据成员被放在相关的程序文本文件中(其文件组织方式与非模板类成员相同),为了使用非内联函数和静态成员,将类模板声明为export。分离编译模式更好的实现类模板接口与实现的分离,接口放在头文件中,相关定义放在程序文本文件中。
8、 类模板的显式实例声明(template class Queue<int>;)后,则其所有成员也被显式实例化,故必须已经提供了类模板及其成员的全部定义。且注意其编译时,必须使用“抑制模板隐式实例化的选项”。
9、 类模板的特化相关:
1、 对类模板实例的一个成员提供一个特化(不使用类模板的通用成员定义),格式为template <> 被特化的类成员名。将该特化在头文件中声明在类模板定义之外,其实现放在程序文本文件中。
2、 对整个类模板进行特化,用在整个类模板定义对于某个特殊的类型并不合适的情况,特化实例的定义与通用类模板的定义完全无关,其也有自己独立的数据成员和成员函数等。注意在同一个程序中,类模板不能在某些文件中根据通用模板定义被实例化,而在其他文件中却针对同一组模板实参被特化。
3、 类模板的部分特化,同样,部分特化的定义也与通用模板的定义完全无关,注意的是,当程序声明了类模板部分特化,编译器则会自动选择“对该实例而言最为特化的模板定义”来进行实例化。