2010-03-26 22 views
1

我試圖創建一個模板「AUTOCLASS」與任意一組的成員,如創建一個任意等級:C++模板:與會員專業化問題

AutoClass<int,int,double,double> a; 
a.set(1,1); 
a.set(0,2); 
a.set(3,99.7); 
std::cout << "Hello world! " << a.get(0) << " " << a.get(1) << " " << a.get(3) << std::endl; 

現在我有一個AUTOCLASS用工作「設置」成員:

class nothing {}; 

template < typename T1 = nothing, typename T2 = nothing, typename T3 = nothing, 
      typename T4 = nothing, typename T5 = nothing, typename T6 = nothing> 
class AutoClass; 

template <> 
class AutoClass<nothing, nothing, nothing, 
       nothing, nothing, nothing> 
{ 
    public: 
    template <typename U> void set(int n,U v){} 
}; 

template < typename T1, typename T2, typename T3, 
      typename T4, typename T5, typename T6> 
class AutoClass: AutoClass<T2,T3,T4,T5,T6> 
{ 
    public: 
    T1 V; 
    template <typename U> void set(int n,U v) 
    { 
     if (n <= 0) 
      V = v; 
     else 
      AutoClass<T2,T3,T4,T5,T6>::set(n-1,v); 
    } 
}; 

我開始有問題實施相應的「得到」。這種方法不會編譯:

template < typename T1, typename T2, typename T3, 
      typename T4, typename T5, typename T6> 
class AutoClass: AutoClass<T2,T3,T4,T5,T6> 
{ 
    public: 
    T1 V; 
    template <typename U> void set(int n,U v) 
    { 
     if (n <= 0) 
      V = v; 
     else 
      AutoClass<T2,T3,T4,T5,T6>::set(n-1,v); 
    } 
    template <typename W> W get(int n) 
    { 
     if (n <= 0) 
      return V; 
     else 
      return AutoClass<T2,T3,T4,T5,T6>::get(n-1); 
    } 
    template <> T1 get(int n) 
    { 
     if (n <= 0) 
      return V; 
     else 
      return AutoClass<T2,T3,T4,T5,T6>::get(n-1); 
    } 
}; 

而且,看來我需要實現獲得的<nothing, nothing, nothing, nothing, nothing, nothing>專業化。任何關於如何解決這個問題的想法?

+0

謝謝大家,我會嘗試使用Boost.Fusion來實現此目的。 – 2010-03-28 18:43:59

回答

3

首先,我更喜歡Boost.FusionBoost.Tuple,因爲它支持我認爲更好的模板元編程和運行時算法混合。

例如,我想向您介紹一個小奇蹟:

struct Name {}; extern const Name name; 
struct GivenName {}; extern const GivenName givenName; 
struct Age {}; extern const Age age; 

class Person 
{ 
public: 
    template <class T> 
    struct value 
    { 
    typedef typename boost::fusion::result_of::at_key<data_type const,T>::type type; 
    }; 

    template <class T> 
    struct has 
    { 
    typedef typename boost::fusion::result_of::has_key<data_type,T>::type type; 
    }; 

    template <class T> 
    typename value<T>::type 
    get(T) { return boost::fusion::at_key<T>(mData); } 

    template <class T> 
    Person& set(T, typename value<T>::type v) 
    { 
    boost::fusion::at_key<T>(mData) = v; return *this; 
    }; 

private: 
    typedef boost::fusion::map < 
    std::pair<Name, std::string>, 
    std::pair<GivenName, std::string>, 
    std::pair<Age, unsigned short> 
    > data_type; 
    data_type mData; 
}; 

這真的很有趣的使用:

Person p; 
p.set(name, "Rabbit").set(givenName, "Roger").set(age, 22); 

好了,我自己的班級喜歡索引比指數,因爲我可以傳達意義以及添加類型檢查;)

+1

我喜歡基於標籤的索引+1。 – 2010-03-26 19:44:30

+0

使用Boost.Fusion集合來保存數據還有另一個好處>它就好像您獲得了反射,因此您可以自動化打印(用於調試),序列化和反序列化,對消息的編碼和解碼等。 – 2010-03-28 11:48:20

3

我可以推薦使用Boost庫的廣泛的(以及經過充分測試和跨平臺的)模板 - magicky類的設置嗎?這聽起來像你正在尋找的是boost::tuple。任何時候你都可以不用編寫自己的代碼—,特別是在模板複雜的情況下—你應該使用別人的。

+0

我知道boost :: tuple。不過,我想添加更多的方法到這個類模板,不在boost :: tuple上。所以我試圖用這種方式來實現它。但我同意通常最好使用已知的庫,而不是重新發明輪子。 – 2010-03-26 17:36:24

+0

您是否嘗試過通過創建新函數來簡單添加額外的功能來增強::元組?你在尋找什麼額外的功能? – 2010-03-26 17:40:14

+0

也許你可以繼承它或者添加額外的模板化函數來實現其他功能。 – 2010-03-26 17:42:16

0

您需要執行<沒有,沒有... >因爲您的基本情況。試想一下:

template <typename W> W get(int n) 
{ 
    if (n <= 0) 
     return V; 
    else 
     return AutoClass<T2,T3,T4,T5,T6>::get(n-1); 
} 

考慮會發生什麼,當你用5正呼籲全AUTOCLASS這種功能,它與5名成員創建了一個AUTOCLASS與N = 4調用... 。而直到再次做到了這一點:

template <typename W> W get(int n) // current autoclass is <T6,nothing,nothing...> 
{ 
    if (n <= 0) 
     return V; 
    else 
     return AutoClass<T2,T3,T4,T5,T6>::get(n-1); // this is <nothing, nothing...> 
} 

當然,這個autoclass的調用不會發生,但編譯器必須編譯該代碼,因爲你已經告訴它了。

您還需要作出AUTOCLASS <什麼都沒有,... > ::得到,因爲n可以取是1093

我看不到出路的這與你當前界面。如果你把n放在模板參數中,你可以創建一個不會這樣做的特例。在這種情況下,你不能。我認爲你會遇到很多問題,因爲你選擇了這個界面將會很難解決。例如,當W是'int'但是AutoClass :: get(n-1)返回一個double或者更糟糕的東西時會發生什麼,這是完全不相容的?

+0

是的,我意識到這個問題。也許你所說的將索引n放入模板中,比如'a.get <2>()'是一個好主意,但我不知道該怎麼做。 – 2010-03-26 17:42:34

+0

您需要編寫至少一個以上的元函數:return_type 。然後你的函數最終會得到簽名:template < int n> typename return_type :: type get();使用到目前爲止您使用的類似方法,但使用模板特化作爲遞歸而不是函數遞歸。 – 2010-03-26 17:57:07

1

正如其他人提到的,您可能應該能夠通過重用Boost或其他地方的現有實現來獲得所需的位置。

如果你會做的東西,不能使用這些來完成,或者如果你很好奇:

  • 儘量保持僞可變參數模板出推行
  • 使用類型列表而是允許遞歸元函數等。
  • 如果需要使用僞可變參數模板作爲一個接口轉發 的實施
  • 做盡可能多的在編譯時有可能,尤其是對指數等

一個簡單的方法檢查,使用MPL爲方便起見,可能是這個樣子:

template<class Types, size_t N> struct holder 
    // recursively derive from holder types: 
    : holder<Types, N-1> 
{ 
    typename boost::mpl::at_c<Types,N>::type value; 
}; 

// specialization that terminates the recursive derivation: 
template<class Types> struct holder<Types,0> { 
    typename boost::mpl::at_c<Types,0>::type value; 
}; 

template<class Types> 
class AutoClass 
    // recursively derive from holder types: 
    : holder<Types, boost::mpl::size<Types>::value-1>  
{ 
    enum { n = boost::mpl::size<Types>::value }; 
public: 
    template<size_t N, class U> void set(const U& u) { 
     // index check at compile time: 
     BOOST_STATIC_ASSERT((N < n)); 
     // cast to responsible holder base: 
     static_cast<holder<Types,N>*>(this)->value = u; 
    } 
    template<size_t N> typename boost::mpl::at_c<Types,N>::type get() const { 
     // index check at compile time: 
     BOOST_STATIC_ASSERT((N < n)); 
     // cast to responsible holder base: 
     return static_cast<const holder<Types,N>*>(this)->value; 
    } 
}; 

用法:

typedef boost::mpl::vector<int,std::string> Types; 
AutoClass<Types> a; 

a.set<0>(42); 
assert(a.get<0>() == 42); 
a.set<1>("abcde"); 
assert(a.get<1>() == "abcde"); 

請記住,爲了最終用戶的便利,這仍然可以使用僞變量模板進行包裝。