C++ 模板特化以及Typelist的相關理解

  近日,在學習的過程當中第一次接觸到了Typelist的相關內容,好比Loki庫有一本Modern C++ design的一本書,大概JD搜了一波沒有譯本,英文版600多R,瞬間從價值上看到了這本書的價值!!這是題外話。這本書十分經典。其內容對於一個C++新手來講須要時間來理解吸取。在這裏記錄一下本身的理解。往後發現錯誤會給予更正。若有有人碰巧看到了。歡迎指正。html

參考了http://blog.csdn.net/gatieme/article/details/50953564ios

整篇內容分了三個部分:1.特化  2.Typelist 3.應用的情形c++

1.在說明Typelist相關內容以前,要先了解一下什麼叫模板特化與模板偏特化。dom

  1.1 模板與特化:函數

    模板分爲函數模板與類模板。函數模板是一種抽象函數的定義,它表明了具備相同結構的一類函數。類模板相似於Stack等封裝區分數據類型,是一種更高級的抽象封裝學習

    所謂特化就是講泛型的東西更加的具體化,好比在某些泛型參數中進行限定,使得不受任何約定的模板參數受到了約束(好比常見的這個大寫T),下面的例子中會更具象化的說明我的的一些理解。測試

    特化的分類:分爲函數模板特化和類模板特化,全特化與偏特化spa

    ①:函數模板特化.net

      當函數模板須要對某些類型進行限定的時候稱之爲函數模板特化設計

    ②:類模板特化

      與上述相似,只是是使用於類

    ③:全特化

      將模板中的參數所有指定爲肯定的類型,其標誌就是應用於徹底肯定的內容。而不是在編譯時去肯定具體的應用實例。標誌: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;

因爲時間關係:上述第三種並無完整的能夠編譯經過的代碼。將在近期補充

相關文章
相關標籤/搜索