首页 > 代码库 > 模板函数的特化

模板函数的特化

  1. 模板特化:就是在实例化模板时,对特定类型的实参进行特殊处理,即实例化一个特殊的实例版本,  
  2. 当以特化定义时的形参使用模板时,将调用特化版本,模板特化分为全特化和偏特化;  
  3. 1. 函数模板的特化,只能全特化;  
  4.   
  5. //泛型版本  
  6. template <class T> int compare(const T &v1, const T &v2)  
  7. {  
  8.   if(v1 < v2) return -1;  
  9.   if(v2 > v1) return 1;  
  10.   return 0;  
  11. }  
  12.   
  13. 对于该函数模板,当实参为两个char指针时,比较的是指针的大小,而不是指针指向内容的大小,此时就需要为该函数模板定义一个特化版本,即特殊处理的版本:  
  14.   
  15. //为实参类型 const char * 提供特化版本  
  16. template <> int compare<const char *>(const char * const &v1, const char * const &v2)  
  17. {  
  18.   return strcmp(v1, v2);  
  19. }  
  20.   a: template <> //空模板形参表  
  21.   b: compare<const char *> //模板名字后指定特化时的模板形参即const char *类型,就是说在以实参类型 const char * 调用函数时,将产生该模板的特化版本,而不是泛型版本,也可以为其他指针类型定义特化版本如int *.  
  22.   c: (const char * const &v1, const char * const &v2)//可以理解为: const char * const &v1, 去掉const修饰符,实际类型是:char *&v1,也就是v1是一个引用,一个指向char型指针的引用,即指针的引用,加上const修饰符,v1就是一个指向const char 型指针的 const引用,对v1的操作就是对指针本身的操作,操作方式与指针一致,比如*v1,是正确的;//注意这里的const char *, 由于形参是一个指向指针的const引用,所以调用特化版本时的实参指针类型(并非存储的数据的类型)可以为const也可以为非const,但是由于这里形参指针指向的数据类型为const char *(强调存储的数据是const),所以实参指针所指向的数据类型也必须为const,否则类型不匹配;  
  23.     
  24. //特化版本 (int *)  
  25. template <> int compare<const int *>(const int * const &v1, const int * const &v2)//v1 和 v2 是指向const 整形变量的const引用;  
  26. {  
  27.   if(*v1 < *v2) return -1;//像指针一样操作,可以理解v1,v2就是指针,因为它是指针的引用;  
  28.   if(*v2 > *v1) return 1;  
  29. }  
  30.   
  31.  2. 与其他函数声明一样,应该在一个头文件中包含模板特化的声明,在使用特化模板的源文件中包含该头文件;  
  32.  注意,函数模板调用时的实参与模板形参不进行常规转换,特化与泛型版本都不进行常规转换,类型必须完全一致,非函数模板在实参调用时进行常规转换;普通函数和函数模板调用时的实参与模板形参都进行两种转换:  
  33.  (1). const转换:接受const引用或者const指针的函数,可以分别以非const对象的引用或者指针来调用,无需产生新实例,如果函数接受非引用类型或者非指针类型,形参类型和实参类型忽略const,无论传递const还是非const对象给非引用类型的函数,都使用相同的实例;  
  34.  (2). 数组或函数指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规转换,数组转换为指向实参第一个元素的指针,函数实参当做指向函数类型的指针;注意:函数模板中,模板形参表中的非类型形参遵循常规转换原则。  
  35.   
  36. //例子:  
  37. //16.6.1.h  
  38.   
  39. #include <stdio.h>  
  40. #include <string.h>  
  41. #include <iostream>  
  42.   
  43. //泛型版本  
  44. template <typename T> int compare(const T &v1, const T &v2)  
  45. {  
  46.   std::cout << "template <typename T>" << std::endl;  
  47.   if(v1 < v2) return -1;  
  48.   if(v2 < v1) return 1;  
  49.   return 0;  
  50. }  
  51.   
  52. //为实参类型 const char * 提供特化版本  
  53. //template <> int compare(const char * const &v1, const char * const &v2) //省略了函数名后边的显示模板实参,因为可以从函数形参表推断出来,本定义与下边的定义都是正确的;  
  54. template <> int compare<const char *>(const char * const &v1, const char * const &v2)  
  55. {  
  56.   std::cout << "template <> int compare<const char *>" << std::endl;  
  57.   return strcmp(v1, v2);  
  58. }  
  59.   
  60. //为实参类型 char * 提供特化版本  
  61. //template <> int compare(char * const &v1, char * const &v2)  
  62. template <> int compare<char *>(char * const &v1, char * const &v2)  
  63. {  
  64.   std::cout << "template <> int compare<char *>" << std::endl;  
  65.   return strcmp(v1, v2);  
  66. }  
  67.   
  68. //16.6.1.cpp  
  69. #include <iostream>  
  70. #include "16.6.1.h"  
  71. using namespace std;  
  72. int main()  
  73. {  
  74.   cout << compare(1, 2) << endl;  //根据实参类型进行实参推断,将为该调用实例化int compare(int, int)  
  75.   char a[] = {"abc"}; //一个普通字符数组,不是指针,形参为引用时,数组大小成为形参的一部分,数组不转换为指针,类型不匹配;  
  76.   const char b[] = {"abc"}; //一个常量字符数组,不是指针,类型不匹配;  
  77.   char *p = "ddd"//一个非const指针,指向非const数据,但是特化版本的形参类型是一个指向const数据的const引用,强调了指针指向的数据类型是const,也就是说实参指针指向的数据类型必须是const即指针存储的数据必须是const的,但这里不是因此类型不匹配;  
  78.   char * const pc = "ddd"//一个const指针,指向非const数据,类型不匹配,原因同上,和指针是否是const没关系,和指针存储的数据类型有关;  
  79.   const char * const pc = "ddd"//一个const指针,指向const数据,满足特化版本的形参(一指向const数据的const引用),类型匹配;  
  80.   const char * pc = "ddd"//一个非const指针,指向const数据,类型匹配,原因同上;  
  81.   
  82.   //为实参类型 const char * 提供特化版本  
  83.   const char *pa = "abc"// 或者 const char * const pa = "abc"; 与指针指向数据类型是const还是非const有关,而与指针是const还是非const没关系,因为,特化版本的形参类型是一个指向指针的cosnt引用,因此不会改变指针的值,所以指针本身是const还是非cosnt没有关系,但是由于特化版本形参的引用指向的指针所指向的数据类型是const,所以不能使用一个指向非const数据的指针调用特化版本,因为数据类型不匹配;  
  84.   const char *pb = "bbd";  
  85.   cout << compare(pa, pb) << endl; // 根据实参类型为该调用实例化特化版本int compare(const * const &v1, const * const &v2), 函数模板调用时的实参与模板形参不进行常规转换;由于编译器对特化版本不进行实参与形参的常规转换,所以调用的实参类型必须与特化版本的声明完全一致,否则将从泛型版本进行实例化,或者函数匹配错误;由于compare声明的形参都是const char *即char *型指针存储的是const数据,所以不能传递一个存储了非const数据的char *型指针(尽管此时的cosnt char *形参不会改变实参指针指向的值),也不能传递一个const数组名字(此时数组名不会转换为指针),必须传递一个指向const数据的指针,即代码中的 const char *pa,类型必须完全匹配;  
  86.   
  87.   //为实参类型 char * 提供特化版本  
  88.   char *pc = "ccc";  
  89.   char *pd = "ddd";                                                                             
  90.   cout << compare(pc, pd) << endl;  
  91.   return 0;  
  92.   
  93.   //char * 与 const char * 是两个不同的数据类型(前者存储的数据是常量与后者存储的数据是非常量),虽然可以将类型 char * 通过常规转换,转换成 const char *,但是作为模板实参,在模板实参推断时,不会把函数调用时的实参类型 char * 转换为模板形参类型const char *,所以必须提供两个特化版本。  
  94.  }  
  95.   
  96.   运行结果:  
  97.   template <typename T>  
  98.   -1  
  99.   template <> int compare<const char *>  
  100.   -1  
  101.   template <> int compare<char *>  
  102.   -1