本文,我們將介紹一種利用模板匹配來實現類型甄別的技術。
首先,類型是數值的抽象,那么我們從最簡單的編譯期數值甄別開始看,如果你寫下了下面這樣一段代碼:
1 int main() 2 { 3 int i = 10; 4 i++; 5 bool b = i > 0 ? true:false; 6 if(b){ 7 cout<<"TRUE"<<endl; 8 }else{ 9 cout<<"FALSE"<<endl; 10 } 11 }
上面的代碼,對於b的值來說,是編譯期就能確定的,肯定是true。
試想,如果這段代碼如果用於處理互聯網上的請求,每一次請求都調用上面的代碼片段時,響應程序每次都會進行如上“復雜”的運算。
但是,有必要嗎?沒有必要,上面的運算只需要運算一次就夠了,那如何使得只運算一次呢?讓程序在編譯期決定運算結果,是的。
幸好,目前很多編譯器都有這個智能的優化,當代碼被匯編為匯編碼時,編譯器就已經為我么做了優化了,當然,本文並不討論這個,本文討論的是數值的抽象,類型的編譯期甄別。
為了解編譯期甄別,我們先介紹運行期類型甄別的方法,這里用一個關鍵函數typeid來實現運行期類型甄別,看如下運用typeid函數的代碼:
1 int main() 2 { 3 const char* intType = "int"; 4 5 int i = 10; 6 const char* iType = typeid(i).name(); 7 if(!strcmp(intType, iType)){ 8 cout<<"i type is : "<<"int"<<endl; 9 }else{ 10 cout<<"i type is : "<<"unknown"<<endl; 11 } 12 }
上面的代碼用typeid函數獲取了i的類型,但是必須要等到運行期才能決定,那如何能在編譯期就決定呢?讓我們來進入今天的主題:用模板匹配實現編譯期類型甄別。
首先要明確我們要干什么,我們希望類型在編譯期就能判斷,數值的抽象是類型,而類型的抽象是模板。於是我們想到用模板來實現。
當我們使用模板的時候,編譯期是就已經能決定要使用何種模板的,比如一下代碼:
// 函數模板 template <typename T> void foo(T){ cout<<"unknown"<<endl; } // int類型的特化 template <> void foo(int){ cout<<"int"<<endl; } int main() { int i = 10; double d = 12.2; foo<int>(i); foo<double>(d); }
上面當我們調用foo<int>(i);時,可以看到使用的是int類型特化的模板函數。決定該調用哪個函數是在編譯期就決定下來的,那我們要如何利用這個特性呢?
1 template<typename T> 2 class ClassChecker 3 { 4 public: 5 typedef struct { char c[2];} Yes; 6 typedef struct { char c[1];} No; 7 static Yes _Check(T t); // 如果類型T和函數的T相同,則調用該函數,返回Yes,即長度為2的結構體 8 static No _Check(...); // 不匹配時,調用該函數,返回No,即長度為1的結構體 9 }; 10 // 這個宏的功能就是利用模板的匹配來看類型與數值是否是同一類型的 11 #define TYPE_CHECKER(T,i) ((sizeof ( ClassChecker<T>::_Check(i)))==2?true:false) 12 13 int main() 14 { 15 int i = 10; 16 if(TYPE_CHECKER(int, i)){ 17 cout<<"type is int"<<endl; 18 } 19 }
上面的代碼就運用了模板推演在編譯期就確定了數值的類型的,這里不做詳細解釋,
上面的代碼不只可以對基本類型進行推演,對復雜的繼承關系的類也能進行編譯期推演,如下:
1 template<typename T> 2 class ClassChecker 3 { 4 public: 5 typedef struct { char c[2];} Yes; 6 typedef struct { char c[1];} No; 7 static Yes _Check(T t); 8 static No _Check(...); 9 }; 10 // 這個宏的功能就是利用模板的匹配來看類型與數值是否是同一類型的 11 #define TYPE_CHECKER(T,i) ((sizeof ( ClassChecker<T>::_Check(i)))==2?true:false) 12 13 class A{}; 14 class B:public A{}; 15 class C:public B{}; 16 class D:public C{}; 17 18 int main() 19 { 20 21 B b; 22 if(TYPE_CHECKER(D,b)) 23 { 24 cout<<"D"<<endl; 25 } 26 else if(TYPE_CHECKER(C,b)) 27 { 28 cout<<"C"<<endl; 29 } 30 else if(TYPE_CHECKER(B,b)) 31 { 32 cout<<"B"<<endl; 33 } 34 else if(TYPE_CHECKER(A,b)) 35 { 36 cout<<"A"<<endl; 37 } 38 }
上面的代碼要注意的是,要從底層子類開始推演,否則得不到結果,原因很簡單,A的子類可以用A的指針來指向,所以會發生問題。