2015-01-10 16 views
1

最近我不得不設計一個建立在另一個模板類之上的類。種類:如何使用策略在類中添加額外的功能層?

template< 
    typename T1, 
    template <class> class A, 
    ..> 
class B { 
    A<T1> a1; 
    // other members; 
public: 
    bool insert(T1 const & value); 
    bool update(T1 const & value); 
    bool delete(T1 const & value); 
    bool contains(T1 const & value); 
}; 

我原來的計劃是隻提供價值語義和這個接口就好。最近的一項要求出現了,我的任務是提供除現有功能之外的鍵控查詢。我正在尋找解決方案,以乾淨的方式做到這一點。

只需在bool find(KeyType常量&鍵值,ValueType &值)中添加似乎不正確。是否有鍵控查找應該依賴於用戶將決定的一些策略 - 所以查找方法本身將根據策略而存在。

如何做到最好?

+0

我2美分:具有相同的類作爲兩個一個集合和一個關聯數組不應該通過策略來處理。這兩個數據結構有不同的目的和語義。 –

+0

「策略」是指作爲模板參數傳遞的布爾值,對吧? – 5gon12eder

回答

1

有多種方法可以解決這個問題,但我認爲這可能適用於您。這個想法是你定義了一個基類,你的類(B)從中繼承。基類是帶有一個bool參數的類模板,並且根據此參數的值確定或未定義find成員。作爲模板參數傳遞的值取決於您爲幾種可能類型A定義的類型特徵。

Warning:template metaprogramming below。

讓我們從類型特徵開始。我假設可能類型的A(這是一個模板的模板參數)是一樣的東西vectormap等:

template <template <typename ...> class Container> 
struct EnableKeyFind 
{ 
    static int const value = false; // fallback                                   
}; 

template <> 
struct EnableKeyFind<std::map> 
{ 
    static int const value = true; 
}; 

template <> 
struct EnableKeyFind<std::unordered_map> 
{ 
    static int const value = true; 
}; 

在話,上述狀態,應該有限定的find構件時A == std::mapA == std::unordered_map。在所有其他情況下,非專門化模板是安裝的,其中value == false,即沒有find成員。您可以添加其他專業化來擴展此原則。

接下來,讓我們定義的基類:

template <typename Container, bool Enable> 
class Base 
{ 
    Container &d_cont; // Base-class should have access to the container                             
public: 
    Base(Container &cont): d_cont(cont) {} 

    template <typename KeyType, typename ValueType> 
    bool find(KeyType const &key, ValueType const &val); 

    /* An alternative implementation would be where you use 
    Container::key_type and Container::value_type and get rid of 
    the function-template. This would require the Container-class 
    to have STL-like semantics. Pick whichever you prefer. */ 
}; 

// Specialization for 'false': no implementation                                   
template <typename Container> 
class Base<Container, false> 
{ 
public: 
    Base(Container &cont) {} 
}; 

基類是專門針對該Enable模板非類型參數是false的情況。在這種情況下,它的實現幾乎是微不足道的(除了構造函數仍然需要相同的簽名)。

現在讓我們來看看修改後的B類(我不停的非描述性的名稱貼近的問題):

template <template <typename ...> class A, typename ... Types> 
class B: public Base<A<Types ...>, EnableKeyFind<A>::value> 
{ 
    typedef A<Types ...> AType; 
    typedef Base<AType, EnableKeyFind<A>::value> BaseType; 

    AType a1; 
public: 
    B(): 
    BaseType(a1) // provide the base with access to a1 
    {} 
}; 

因爲不同的容器類型,如vectormap,需要不同數量我通過使它成爲一個可變參數模板來概括類模板。它現在可以存儲任何類型的容器。它繼承自Base,並使用類型特徵EnableKeyFind<A>來確定它是否應繼承自Base<Container, true>或(專業化)Base<Container, false>

一個典型的用例:

int main() 
{ 
    B<vector, int> b1; 
    b1.find(1, 1); // error: has no member find                                    

    B<map, string, int> b2; 
    b2.find(1, 1);  
} 

我希望這有助於。無論如何,這是一個有趣的小練習。

此方法的一個可能的缺點可能是您的用戶應該專門爲他們的特定容器專門設計了EnableKeyFind類模板,前提是他們希望find()可用。我不知道這是否太多問你的用戶,但你可以隨時提供給他們一個宏,如果你想隱藏的細節:

define ENABLE_KEYFIND(Container) \ 
    template <>\ 
    struct EnableKeyFind<Container>\ 
    { static int const value = true; }; 
相關問題