模板函式的特化

期待一片自己的藍天發表於2014-08-14
  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   

相關文章