近日,在學習的過程中第一次接觸到了Typelist的相關內容,比如Loki庫有一本Modern C++ design的一本書,大概JD搜了一波沒有譯本,英文版600多R,瞬間從價值上看到了這本書的價值!!這是題外話。這本書十分經典。其內容對於一個C++新手來說需要時間來理解吸收。在這里記錄一下自己的理解。日后發現錯誤會給予更正。如有有人碰巧看到了。歡迎指正。
參考了http://blog.csdn.net/gatieme/article/details/50953564
整篇內容分了三個部分:1.特化 2.Typelist 3.應用的情形
1.在說明Typelist相關內容之前,要先了解一下什么叫模板特化與模板偏特化。
1.1 模板與特化:
模板分為函數模板與類模板。函數模板是一種抽象函數的定義,它代表了具有相同結構的一類函數。類模板類似於Stack等封裝區分數據類型,是一種更高級的抽象封裝
所謂特化就是講泛型的東西更加的具體化,比如在某些泛型參數中進行限定,使得不受任何約定的模板參數受到了約束(比如常見的這個大寫T),下面的例子中會更具象化的說明個人的一些理解。
特化的分類:分為函數模板特化和類模板特化,全特化與偏特化
①:函數模板特化
當函數模板需要對某些類型進行限定的時候稱之為函數模板特化
②:類模板特化
與上述類似,只是是使用於類
③:全特化
將模板中的參數全部指定為確定的類型,其標志就是應用於完全確定的內容。而不是在編譯時去確定具體的應用實例。標志:template<>然后是和末班類型沒有關系的類實現或者函數定義。
④:偏特化
模板中的參數沒有被全部指定。需要編譯器在編譯時進行確定。
1.2 函數模板特化:
如下代碼:
template <class T> int compare(const T left,const T right) { std::cout << "test template func" << endl; return (left - right); }
這個函數能夠滿足一些基本類型的比較需求(int,float,....巴拉巴啦),但是對於字符串的比價這個函數是不能支持的。
因此我們可以對其進行特化處理。
template < > int compare<const char *>(const char * left,const char * right) { std::cout << "function tempate special" << std::endl; return strcmp(left,right); }
//另一種特化方式是如下
template < >
int compare(const char * left,const char * right) {
std::cout << " in special template <> .." << std::endl;
return strcmp(left,right);
}
測試代碼:
#include<bits/stdc++.h> template <class T> int compare(const T left,const T right) { std::cout << "test template func" << std::endl; return (left - right); } template <> int compare(const char* left, const char* right){ std::cout <<"in special template..." <<std::endl; return strcmp(left, right); } int main() { std::cout << compare(1, 2) << std::endl; const char *left = "abcd"; const char *right = "accd"; std::cout << compare(left, right) << std::endl; return 0; }
輸出內容
test template func -1 in special template... -1
函數的特化,當函數調用發現有特化后的匹配函數的時候,會優先調用特化的函數。而不是通過函數模板進行實例化。
1.2類模板特化
與函數模板特化類似,當模板內需要對某些類型進行特別處理時,需要這種處理。這里歸納了一個模板參數的類模板特化的幾種類型。
1.絕對類型
2.引用,指針類型
3. 特化為另一個類模板(這個厲害了,我猜的)
1.2.1 特化為絕對類型 : 直接為某個特定類型做特化,這是一種常見的方式。
#include <iostream> #include <cstring> #include <cmath> template <class T> class Compare { public : static bool IsEqual(const T & lh,const T & rh) { std::cout << "uniusall " << std::endl; return lh == rh; } }; template<> class Compare<float> { public : static bool IsEqual(const float & lh,const float & rh) { std::cout << "float special class " << std::endl; return abs(lh - rh) < 1e-4; } }; int main() { Compare<int> comp1; std::cout << comp1.IsEqual(2,3) << std::endl; Compare<float> comp2; std::cout << comp2.IsEqual(1,1) << std::endl; }
另外我特意要說明的是,如果沒有第一段template<class T>的模板聲明,直接template<> Class Compare<float>是否可以?
這個是不可以的,編譯報錯內容是 'Compare' is not a class template,這個在后邊有關typelist內容中也會提出(ps 主要是由在閱讀Typelist中的一行代碼導致我特意測試了一下這種情況)。
偏特化:
template <class T1,class T2> class A {}; template <class T1> class A<T1,int> {};
下面的代碼框內容是在另一個博客中提到的另外2中類型,目前還沒有使用過。作為記錄放在這里
template <class _Iterator> struct iterator_traits { typedef typename _Iterator::iterator_category iterator_category; typedef typename _Iterator::value_type value_type; typedef typename _Iterator::difference_type difference_type; typedef typename _Iterator::pointer pointer; typedef typename _Iterator::reference reference; }; // specialize for _Tp* template <class _Tp> struct iterator_traits<_Tp*> { typedef random_access_iterator_tag iterator_category; typedef _Tp value_type; typedef ptrdiff_t difference_type; typedef _Tp* pointer; typedef _Tp& reference; }; // specialize for const _Tp* template <class _Tp> struct iterator_traits<const _Tp*> { typedef random_access_iterator_tag iterator_category; typedef _Tp value_type; typedef ptrdiff_t difference_type; typedef const _Tp* pointer; typedef const _Tp& reference; };
// specialize for T* template<class T> class Compare<T*> { public: static bool IsEqual(const T* lh, const T* rh) { return Compare<T>::IsEqual(*lh, *rh); } };
這種特化其實是不一種絕對的特化,他只是對類型做了某些限定,但仍然保留了莫版型,給我們提供了極大地方便。
在這里,我么不就不需要對int*.float *,double *等等類型分別做特定的特化。這其實是第二種方式的擴展,其實夜視對類型做了某種限定。而不是絕對化為某個具體類型。
如下一段代碼
template <class T> class Compare< vector<T> > { public : static bool IsEqual(const vector<T> &lh,const vector<T> & rh) { if (lh.size() != rh.size()) return false; else { for (int i = 0 ; i < lh.size() ; i++) if (lh[i] != rh[i]) return false; } } };
上述的代碼比較好理解。就省略了
以下是第三種特化為另一個類模板
template <class T1> struct SpecializedType { T1 x1; T1 x2; } template <class T> class Compare< SpeciallizedType<T> > { public : static bool IsEqual(const Specialized<T> & lh,const Specialized<T>&rh) { return Compare<T>::IsEqual(lh.x1 + lh.x2,rh.x1 + rh.x2); } };
SpecializedType<float> a = {10.0f,10.1f};
SpecializedType<float> b = {10.3f,10.4f};
bool flag = Compare<SpecializedType<float> >::IsEqual(a,b);
2.關於TypeList,這個是來自於Loki庫中的一部分。
基於個人的理解。我分開一段一個一個函數的記錄一下。
首先是一些最基本的定義和宏
class NullType {}; template <class T,class U> struct Typelist { typedef H Head; typedef U Tail; } //通過定義一些宏使得typelist線性化 #define TYPELIST_0() NullType #define TYPELIST_1(T1) Typelist<T1,TYPELIST_0()> #define TYPELIST_2(T1,T2) Typelist<T1,TYPELIST_1(T2)> #define TYPELIST_3(T1,T2,T3) Typelist<T1,TYPELIST_2(T2,T3)> #define TYPELIST_4(T1,T2,T3,T4) Typelist<T1,TYPELIST_3(T2,T3,T4)> #define TYPELIST_5(T1,T2,T3,T4,T5) Typelist<T1,TYPELIST_4(T2,T3,T4,T5)>
Typelist結構里面是2個typedef,看見其內部沒有任何數值,他們的實體是空的,不含有任何狀態,也未定義任何函數。執行期間Typelists也不帶任何數值,他們的存在只是為了攜帶類別信息,Typelist並未打算被具體化。 另外規定,typelist必須以NullType作為結尾。其可以被視為一個結束符號。具體宏的作用結合下面的例子來說明。
//如何聲明使用 typedef TYPELIST_0() TL0; typedef TYPELIST_3(char,int,double) TL3;
將上面的宏產開后 是如下的形式 typedef NullType TL0; typedef Typelist<char,Typelist<int,Typelist<double,NullType> > >TL3;
上面這種方法利用了特化中的特化為另一個模板的方法。
針對於上面的展開,可以看下獲取長度length的代碼
//為了方便解釋,我把他們分為3部分. //第一部分 只有一行 template<class TList>struct Length; //第二部分 template<>struct Length<NullType> { enum { value = 0; } }; //第三部分 template<class T,class U> struct Length<Typelist<T,U> > { enum { value = 1 + Length<U>::value} ; };
分開來解釋一個我個人的理解:
第一部分:template<class TList>struct Length;
這句話實際上是最困擾我的一句話,首先這句話一定要有。否則的代價是編譯不過。
關於這句話的作用個人的理解第一:基於編譯是否通過,如果使用全特化,必然要有“前置的模板聲明”,否則會報錯
第二:首先說明這個獲取長度的方法該如何調用。基於前面講的TL3
std::cout<<Length<TL0>::value<<std::endl;
借助這個調用來解釋我個人的理解,在Length中,只有NullType和Typelist<T,U>可以進行匹配,當我們嘗試傳遞Length<int>的時候自然是無法找到匹配, 原因是Length進行特化的時候只能匹配到NullType和Typelist,
由此,這句話既是聲明又是一種限定,他告訴編譯器什么形式的具象化可以匹配模板。
第二個部分:
全特化,只有NullType可以匹配,遞歸調用的終點。很好理解
第三個部分:
偏特化,結合前面的宏展開,可以看出其遞歸調用的方式。其使用Typelist來進行特化,需要2個參數。
剩下的代碼大體只是邏輯區別:不再贅述,完整代碼:(來自http://blog.csdn.net/zhuyingqingfen/article/details/43938713)
#ifndef TYPE_LISTS_H_ #define TYPE_LISTS_H_ #include <iostream> #include <string> #include "typetraits.h" /* TypeLists 內部沒有任何數值(value),他們的實體是空的,不含有任何狀態,也未定義任何函數。 執行期間TypeLists也不帶任何數值,他們存在的理由只是為了攜帶型別信息。TypeLists 並未打算被具 現化。因此,當我們說“a TypeListL”,實際指的是一個typelist型別,不是一個typelist 對象。 規定 typelist 必須以NullType(類)結尾,NullType可被視為一個結束符號,類似於c字符串的\0功能, 定義一個只有一個元素的typelist如下: typedef Typelist<int,NullType> OneTypeOnly. */ template<class T,class U> struct Typelist { typedef T Head; typedef U Tail; }; Class NullType{}; //通過定義宏 將typelist線性化 #define TYPELIST_0() NullType #define TYPELIST_1(T1) Typelist<T1,TYPELIST_0()> #define TYPELIST_2(T1,T2) Typelist<T1,TYPELIST_1(T2)> #define TYPELIST_3(T1,T2,T3) Typelist<T1,TYPELIST_2(T2,T3)> #define TYPELIST_4(T1,T2,T3,T4) Typelist<T1,TYPELIST_3(T2,T3,T4)> #define TYPELIST_5(T1,T2,T3,T4,T5) Typelist<T1,TYPELIST_4(T2,T3,T4,T5)> //計算TypeList長度 //大多數Typelist的操作都是基於遞歸,遞歸終止條件通過模板特化實現。 template<class TList>struct Length; template<>struct Length<NullType>//Length的全特化,即,只匹配NullType。 { enum{value = 0}; }; template<class T,class U> struct Length<Typelist<T,U> >//Length的扁特化,可匹配任何TypeList<T,U>類型,包括U同時也是Typelist的復合情況。 { enum{value = 1+Length<U>::value}; }; //2 索引式訪問 template <class TList,unsigned int index> struct TypeAt; template<class Head,class Tail> struct TypeAt<Typelist<Head,Tail>,0> { typedef Head Result; }; template<class Head,class Tail,unsigned int i> struct TypeAt<Typelist<Head,Tail> ,i> { typedef typename TypeAt<Tail,i-1>::Result Result; }; //類似TypeAt功能,不過TypeAtNonStrict對逾界訪問更加寬容。 //比如TypeList的個數是3,那么你不能使用TypeAt<TL3,3>::Result,這樣會編譯錯誤。 //但是TypeAtNonStrict<TL3,3,NullType>::Result可以,如果不存在索引為3的type,那么結果是第三個引數即NullType template <class TList, unsigned int i, typename DefType = NullType> struct TypeAtNonStrict { typedef DefType Result; }; template <class T, class U, typename DefType> struct TypeAtNonStrict< Typelist<T, U>, 0, DefType > { typedef T Result; }; template <class T, class U, unsigned int i, typename DefType> struct TypeAtNonStrict< Typelist<T, U>, i, DefType > { typedef typename TypeAtNonStrict<U, i - 1, DefType>::Result Result; }; //3 查找TypeList template<class TList,class T> struct IndexOf;//聲明 template<class T> struct IndexOf<NullType,T>//如果TList為NullType,那么令value = -1; { enum{value = -1}; }; template<class Tail,class T> struct IndexOf<Typelist<T,Tail> ,T>//如果T是TList中的頭端,那么令value= 0; { enum{value = 0}; }; template<class Head,class Tail,class T>//將IndexOf施於TList尾端和T,並將結果置於一個臨時變量temp struct IndexOf<Typelist<Head,Tail> ,T>//如果temp為-1,令value為-1,否則令value為1+temp { private: enum{temp = IndexOf<Tail,T>::value};//temp要先於value聲明定義。 public: enum{value = temp == -1 ? -1 : temp + 1}; }; //4 附加元素到typelist template <class Tlist,class T>struct Append;//聲明 template<>struct Append<NullType,NullType>//如果TList是NULL而且T是NULL,那么令Result為NullType { typedef NullType Result; }; template <class T> struct Append<NullType,T> //如果TList是NullType,且T是type(非typelist), { //那么Result將是"只含有唯一元素的T"; typedef TYPELIST_1(T) Result; }; template <class Head,class Tail> struct Append<NullType,Typelist<Head,Tail> >// 如果TList是NullType,且T是一個typelist,那么Result便是T本身 { typedef Typelist<Head,Tail> Result; }; template<class Head,class Tail,class T>//否則,如果Tlist是non-null,那么result將是個typelist,以TList::Head struct Append<Typelist<Head,Tail>,T> //為起頭端,並以T附加到TList::Tail的結果為其尾端。 { typedef Typelist<Head,typename Append<Tail,T>::Result> Result; }; //5 Reverse template <class TList> struct Reverse; template <>struct Reverse<NullType> { typedef NullType Result; }; template <class Head, class Tail> struct Reverse< Typelist<Head, Tail> > { typedef typename Append< typename Reverse<Tail>::Result, Head>::Result Result; }; #endif
調用測試代碼:
void typelists_test() { typedef TYPELIST_0() TL0; typedef TYPELIST_3(char,int,double) TL3; typedef TYPELIST_3(char,int,double) TL3_1; //Length std::cout<<Length<TL0>::value<<std::endl; std::cout<<Length<TL3>::value<<std::endl; //TypeAt typedef TypeAt<TL3,0>::Result Parm1; typedef TypeAt<TL3,1>::Result Parm2; typedef TypeAt<TL3,2>::Result Parm3; typedef TypeAtNonStrict<TL3,3,EmptyType>::Result TEST_TYPE; std::cout<<"Parm1 Type:"<<typeid(Parm1).name() <<" sizeof : "<< sizeof(Parm1)<<std::endl; std::cout<<"Parm2 Type:"<<typeid(Parm2).name() <<" sizeof : "<< sizeof(Parm2)<<std::endl; std::cout<<"Parm3 Type:"<<typeid(Parm3).name() <<" sizeof : "<< sizeof(Parm3)<<std::endl; std::cout<<"TEST_TYPE Type:"<<typeid(TEST_TYPE).name() <<" sizeof : "<< sizeof(TEST_TYPE)<<std::endl; //IndexOf std::cout<<"char indexof TL3 :"<<IndexOf<TL3,char>::value<<std::endl; std::cout<<"int indexof TL3 :"<<IndexOf<TL3,int>::value<<std::endl; std::cout<<"float indexof TL3 :"<<IndexOf<TL3,float>::value<<std::endl; //Append typedef Append<TL3,int> TL4;//TL4不是一個TypeList typedef Append<TL3_1,TYPELIST_2(float,double)> TL5; std::cout<<"TL4 Length :"<<Length<TL4::Result>::value<<std::endl; std::cout<<"TL5 Length :"<<Length<TL5::Result>::value<<std::endl; //Reverse std::cout<<"Reverse result:"<<typeid(Reverse<TL3>::Result).name()<<std::endl; }
3.應用
舉例:大學績點計算,眾所周知的這是一個基於權重的計算方法,對於不同學分的學科權重不同。現在假設有4科目吧:信號,電磁場,高頻,微波器件
那么可能的實現方式是這樣的:(PS覺得這個例子不太恰當甚至十分不恰當,僅作為個人輔助理解。)
①:直接計算
//偽代碼 if (當前的科目是信號) { 信號科目相關加權和分數處理... } if (當前的科目是電磁場) { 電磁場科目相關加權和分數處理... } //剩下的略
②:利用類和繼承來實現。
第一種的實現很直接卻脫離了面向對象的設計,顯得代碼十分多,亂。
第二種大體代碼就像下面,在這里創建了對應的實例化對象進行處理。
#include<bits/stdc++.h> using namespace std; class ScoreBase { public : ScoreBase() {} virtual int calcuWeight(score * scores); virtual ~ScoreBase() {} } class ScoreSignal : public ScoreBase { public : ScoreSignal(){} virtual int calcuWeight(score * scores); } class ScoreElect : public ScoreBase { public : ScoreElect() {} virtual int calcuWeight(score * scores); } vector<ScoreBase *>dealscore; dealscore.push_back(new ScoreSignal()); dealscore.push_back(new ScoreElect()); score * scores = get_scores();//獲取到了分數
int totalWeight = 0; for (int i = 0 ; i < (int)dealscore.size() ; i++) totalWeight += dealscore[i] -> calcuWeight(scores);
③:使用typelist完成這件事情
記得之前的展開宏么,這就可以使用到它了。為了方便,我們用結構體(純粹是因為默認public,關於在C++中struct和class的區別,http://www.cnblogs.com/Commence/p/7481315.html)
第一步先通過宏定義出我們想要的東西
struct signalscore { int static calcuWeight(score * scores); } struct elecscore { int static calcuWeight(score * scores); } struct highfreqscore { int static calcuWeight(score * scores); } typedef Typelist<signalscore,Typelist<elecscore,Tyeplist<highfreqscore,NullType> > >calWeightList;
第二步:類似前面獲取Length的方法,建立模板來處理它。
template<class TList>struct calWeight; template<>struct calWeight<NullType> { int static calcuWeight(scene * scenes) { return 0;} } template<class T,class U> struct calWeight< Typelist<T,U> > { int static calWeight(scene * scenes) { return T::calcuWeight(scene * scenes) + calWeight<U>::calWeight(scene * scenes); } }
std::cout << calWeight<calWeightList>::calWeight(scenes) << std::endl;
由於時間關系:上述第三種並沒有完整的可以編譯通過的代碼。將在近期補充
