看了546@C++@Freecity之后,發覺非常有意思,由此產生一些想法
很多時候寫一個類的時候,需要多個模版參數,例如一個遺傳算法的算法類,需要一個模版參數來指定交配方式,另一個模版參數來指定子代選擇的方式,還要一個參數來指定變異的方式。那么一般來說,這個類會寫成:
template<class T //描述問題的一個類
, class CrossPolicy = AvgCrossPolicy //雜交方式
, class SelectPolicy = DefaultSelectPolicy //子代選擇的方式
, class VariationPolicy = ReverseVariationPolicy> //變異方式
class Gene
: private AvgCrossPolicy
, private SelectPolicy
, private VariationPolicy
{
....
};
這樣用戶要使用該類的時候,可以直接指定T,就行了,然而如果要指定變異方式,那么就必須把所有的參數都顯式的寫出來,很不方便
546提供了一種有效的方法,可以讓我們僅僅指定變異參數,而不用寫出另兩個Policy
甚至允許我們以任意的順序書寫幾個Policy參數,都不會有問題
預備知識:
TypeList
一個TypeList是一個類型的容器
template <typename Type_, typename Next_>
struct TypeList
{
typedef Type_ Type;
typedef Next_ Next;
};
這就是一個TypeList。
看這個寫法,是不是像一個鏈表?
首先定義一個類型來表示鏈表尾:class NullType{};
現在一個包含了2個類型的TypeList就可以寫為:
TypeList<T1, TypeList<T2, NullType> >
如何在一個TypeList中查找一個類型的子類?
首先要有一個IsDerivedFrom<Base, T>
這個比較簡單
template<class Base, class T>
class IsDerivedFrom
{
struct large{char a[2];};
static char pred(Base*);
static large pred(...);
public:
enum {Is = sizeof(pred((T*)0)) == sizeof(char)};
};
然后FindChild就容易了
template <class List, class Base>
struct FindChild
{
template <bool IsChild>
struct Select
{
typedef typename List::Type Type;
};
template <>
struct Select<false>
{
typedef typename FindChild<typename List::Next, Base>::Type Type;
};
typedef typename Select<IsDerivedFrom<Base, typename List::Type> >::Type Type;
};
當然還要對一些特殊情況進行特化,例如NullType
template <class Base>
struct FindChild<NullType, Base>
{
typedef NullType Type;
};
這里使用NullType來表明沒找到
實際操作:
首先需要給3個Policy3個基類,分別叫
class AvgCrossPolicyBase{};
class SelectPolicyBase{};
class VariationPolicyBase{};
內容為空就行了,這樣也沒有虛函數調用的開銷
然后聲明一個類來表示默認情況:
class DefaultPolicy{};
定義一個宏
#define TYPELIST_3_N(a, b, c) TypeList<a, TypeList<b, TypeList<c, NullType> > >
下面要寫一些選擇器,用于把合適的類型選擇出來,如果沒找到,則要使用默認的類型
template <class List, class Base, class DefaultType>
struct Selector
{
template <class RetType>
struct Judge
{
typedef RetType Type;
};
template<>
struct Judge<NullType>
{
typedef DefaultType Type;
};
typedef typename Judge<typename FindChild<List, Base>::Type >::Type Type;
};
好啦,現在整個類的聲明可以寫為
template<class T
, class CrossPolicy_ = DefaultPolicy
, class SelectPolicy_ = DefaultPolicy
, class VariationPolicy_ = DefaultPolicy //其后的參數用戶不可指定
, class List = TYPELIST_3_N(CrossPolicy_, SelectPolicy_, VariationPolicy_)
, class CrossPolicy = typename Selector<List, CrossPolicyBase, AvgCrossPolicy>::Type
, class SelectPolicy = typename Selector<List, SelectPolicyBase, DefaultSelectPolicy>::Type
, class VariationPolicy = typename Selector<List, VariationPolicyBase, ReverseVariationPolicy>::Type
>
class Gene
: private CrossPolicy
, private SelectPolicy
, private VariationPolicy
{
....
};
其中第4-7個參數(List,CrossPolicy,SelectPolicy和VariationPolicy)是不由用戶指定的,僅僅是為了起一個別名
第一個參數T必須指定,然后2,3,4這3個參數就可以任意的改變順序了
例如,可以寫Gene<T, DefaultSelectPolicy, AvgCrossPolicy>而不會有任何問題
如果不想要最后面幾個參數的話也行,但是代碼就要稍微長一點
而且最好在類里面進行3個typedef
typedef typename Selector<List, CrossPolicyBase, AvgCrossPolicy>::Type CrossPolicy;
等,以便在實現的時候使用
posted on 2006-07-24 01:06
shifan3 閱讀(1010)
評論(9) 編輯 收藏 引用 所屬分類:
template 、
C++