2011-04-01 67 views
0

我正在使用C++模板實現mixins,以支持基(模板)類的一些「擴展」行爲。如何在兩個mixin模板之間實現多態行爲?

template< class Ch > Base {...}; 

template< class T > M1 : public T {...}; 
template< class T > M2 : public T {...}; 
// etc. 

如果另一個mixin存在於繼承圖中,我需要修改一個mixin中的行爲。 (理想情況下,這將是不考慮這一個混合在第一,但我願意接受,他們必須在一個特定的順序得出的限制。)

換句話說,如果我有:

typedef Base<char> base; 
typedef M2< M1<base>> foo; 
typedef M2<base> bar; 

當M1混入時,M2方法的行爲需要改變 - M1提供了需要用於計算M2結果的數據成員。另一方面,M2對這些數據成員做了一定的保證,如果M2沒有混入,這些數據成員是完全無效的。

那麼我的問題是如何實現這個C++ '03?

我懷疑有一種方法使用模板方法和專業化,但我的模板功能不夠強大(但)。


根據dauphic的回答,我試過了,它確實有效。完全複製M2模板的需求非常可怕,我將接下來嘗試使用SFINAE方法。

#include <iostream> 
#include <UnitTest++.h> 

SUITE(Mixin_specialization_tests) { 

    template< typename Ch > struct Base { 
     typedef Ch * pointer_type; 
     pointer_type p; 
    }; 

    template< class T > struct M1 : public T { 
     typedef typename T::pointer_type pointer_type; 
     pointer_type m1p; 
    }; 

    template< class T > struct M2 : public T { 
     typedef typename T::pointer_type pointer_type; 
     pointer_type m2p; 

     int compute() { 
      std::cout << "unspecialized compute()" << std::endl; 
      return 0; 
     } 
    }; 

    template< > 
    template< class B > struct M2< M1<B> > : public M1<B> { 
     typedef typename M1<B>::pointer_type pointer_type; 
     pointer_type m2p; 

     int compute() { 
      std::cout << "specialized compute()" << std::endl; 
      int unused = M1<B>::m1p - m2p; 
      return 1; 
     } 
    }; 

    typedef Base<char> Bch; 
    typedef M1<Bch> M1b; 
    typedef M2<Bch> M2b; 
    typedef M2< M1<Bch> > M2m1b; 

    TEST(unspecialized) { 
     M2b m2b; 
     CHECK_EQUAL(0, m2b.compute()); 
    } 

    TEST(specialized) { 
     M2m1b m2m1b; 
     CHECK_EQUAL(1, m2m1b.compute()); 
    } 
} 

掙扎了一下後,我設法讓SFINAE變種工作爲好。該代碼主要是除了這部分是相同的:

template< class Query > struct is_M1 { 
    typedef char yes, (&no)[ 2 ]; 
    static no has_m1_member(...); 

    template< class T, typename T::pointer_type T::* > struct dummy {}; 
    template< class T > 
    static yes has_m1_member(T*, dummy<T, &T::m1p>* = 0); 
    BOOST_STATIC_CONSTANT(bool, value = 
     (sizeof(has_m1_member((Query *)0)) == sizeof(yes)) 
    ); 
}; 

template< class T > struct M2 : public T { 
    typedef typename T::pointer_type pointer_type; 
    pointer_type m2p; 

    int compute() { 
     return compute_impl<T>(); 
    } 

    template< typename B > 
    typename boost::enable_if< is_M1<B>, int >::type compute_impl() { 
     std::cout << "sfinae: m1-compute" << std::endl; 
     return 1; 
    } 

    template< typename B > 
    typename boost::disable_if< is_M1<B>, int >::type compute_impl() { 
     std::cout << "sfinae: non-m1-compute" << std::endl; 
     return 0; 
    } 
}; 

正如我在下面所提到的,enable_if <>模板不處理布爾表達式(對我來說),所以我去 與disable_if <>,這似乎去做把戲。

這些都不是特別開心,因爲這個代碼將被貢獻給一個目前沒有使用boost的項目,並且由於複製整個模板的選項是維護的噩夢。但他們都完成了這項工作。我將考慮boost解決方案的答案,只是因爲希望boost代碼將成爲標準,因此不那麼令人頭疼。

謝謝大家。

+1

你需要縮進4個空格碼形成,不僅3. :) – Xeo 2011-04-01 21:08:38

+0

感謝,並感謝固定它。你搖滾! – 2011-04-02 12:36:36

回答

1

正如dauphic所建議的,當它的參數爲M1時,您可以對M2類進行部分特化,當然,您也可以對M1執行相同的操作(通過簡單的交錯聲明)。

但是,這會給你非常粗糙的專門化,即你將不得不重新定義M2的所有成員。如果只有少數成員需要改變,這可能很煩人,即這種解決方案不能很好地擴展。不幸的是,令人煩惱的是,C++不提供獨立專門化成員函數的支持。

儘管如此,Sfinae可以幫助你,但它需要更多的工作。下面基本上是一個經典SFINAE方法允許類模板中的成員函數專業化:

#include <iostream> 
#include <boost/utility/enable_if.hpp> 
#include <boost/config.hpp> 

template <class Model> 
struct is_M1 
{ 
    typedef char yes; 
    typedef char (&no)[2]; 

    template <class T, void (T::*)()> 
    struct dummy {}; 

    template <class T> 
    static yes has_m1_function1(T*, dummy<T,&T::function1>* = 0); 
    static no has_m1_function1(...); 

    BOOST_STATIC_CONSTANT(
     bool 
     , value = sizeof(has_m1_function1((Model*)0)) == 1); 
}; 

template <typename T> 
struct Base { T value; }; 

template <typename T> 
struct M1 : public T { 
    void function1() { }; //I presume M1 would have at least one defining characteristic or member function 
}; 

template <typename T> 
struct M2 : public T { 
    void function2() { function2_impl<T>(); }; 
    template <typename B> 
    typename boost::enable_if< is_M1<B>, void>::type function2_impl() { 
    std::cout << "M1 is mixed in T" << std::endl; 
    }; 
    template <typename B> 
    typename boost::enable_if< !is_M1<B>, void>::type function2_impl() { 
    std::cout << "M1 is not mixed in T" << std::endl; 
    }; 
}; 

int main() { 
    M2< M1< Base<int> > > m2m1b; 
    M2< Base<int> > m2b; 

    m2b.function2(); 
    m2m1b.function2(); 
}; 
+0

enable_if <>模板沒有(在我的盒子上,boost爲1.45)接受布爾表達式。我認爲可能有一種方法可以使用enable_if_c <>代替它,但是我切換到了disable_if <>並獲得了它。 – 2011-04-02 12:24:19

1

你的問題很羅嗦,我不確定我是否理解正確,但我會試一試。

我想你想要專門針對模板M1的情況M2

template<class T> class M1 : public T { }; 

template<class T> class M2 : public T { }; 
template<> template<class B> class M2<M1<B> > : public M1<B> { }; 

在這個例子中,M2通過使用嵌套模板(B)以允許其接受M1模板的任何實例專門用於M1<B>

相關問題