2013-08-22 32 views
6

我試圖使用一個簡單的CRTP形式(奇怪的循環模板模式),因爲我有幾個類,每個類都有幾個相關的類,我想要一種綁定方式他們在一起(例如,我有像Widget,Doobry和Whatsit這樣的類,以及相關的類WidgetHandle,DoobryHandle和WhatsitHandle)。編譯與依賴類型的簡單CRTP案件的錯誤

我用來繼承Base的每個類都添加了一個value_type typedef,以便基類可以將其引用爲typename TWrapper::value_type

struct WidgetHandle {}; 

template <typename TWrapper> 
class Base 
{ 
public: 
    Base(typename TWrapper::value_type value_) 
     : value(value_) {} 

    typename TWrapper::value_type value; 
}; 

class Widget : public Base<Widget> 
{ 
public: 
    typedef WidgetHandle value_type; 

    Widget(WidgetHandle value_) : Base<Widget>(value_) {} 
}; 

int main(int argc, char* argv[]) 
{ 

    Widget i(WidgetHandle()); 
    return 0; 
} 

但是,我得到的編譯錯誤:

scratch1.cpp(10): error C2039: 'value_type' : is not a member of 'Widget' 
scratch1.cpp(16) : see declaration of 'Widget' 
scratch1.cpp : see reference to class template instantiation 'Base<TWrapper>' being compiled 
1>   with 
1>   [ 
1>    TWrapper=Widget 
1>   ] 
scratch1.cpp(10): error C2039: 'value_type' : is not a member of 'Widget' 

這是VS2010,雖然我得到類似的錯誤鏗鏘。我在這裏錯過了什麼?

+0

將Widget作爲參數傳遞給'Base'時,'Widget'是不完整的類型。 – jrok

+0

實際上,我沒有在任何地方看到'value_type'。 – lapk

+0

@PetrBudnik它是在'Widget'主體的開始處。 – jrok

回答

1

更改基地的定義採取公頃ndle類型作爲第二個參數來避免循環依賴。

struct WidgetHandle {}; 

template <typename TWrapper, typename HandleType> 
class Base 
{ 
public: 
    typedef HandleType value_type; 

    Base(HandleType value_) 
     : value(value_) {} 

    HandleType value; 
}; 

class Widget : public Base<Widget, WidgetHandle> 
{ 
public: 
    Widget(WidgetHandle value_) : Base(value_) {} 
}; 

int main(int argc, char* argv[]) 
{ 

    Widget i(WidgetHandle()); 
    return 0; 
} 

您還可以使用特徵類來獲取Widget的WidgeHandle類型。

struct WidgetHandle {}; 
class Widget; 

template<class T> 
struct Handle 
{ 
}; 

template<> 
struct Handle<Widget> 
{ 
    typedef WidgetHandle type; 
}; 

template <typename TWrapper, typename HandleType = Handle<TWrapper>::type> 
class Base 
{ 
public: 
    typedef HandleType value_type; 

    Base(HandleType value_) 
     : value(value_) {} 

    HandleType value; 
}; 

class Widget : public Base<Widget> 
{ 
public: 
    Widget(WidgetHandle value_) : Base(value_) {} 
}; 

int main(int argc, char* argv[]) 
{ 

    Widget i(WidgetHandle()); 
    return 0; 
} 
+0

我知道我可以爲模板定義添加額外的類型,但是如果TWrapper具有指定綁定到的類型的方法,那麼很不幸,這是不幸的。 –

+0

使用默認模板參數從traits類中獲取句柄類型 - 然後至少客戶端代碼看起來是一樣的。 – paxos1977

+0

這就是訣竅。在我的例子中,每個類都有幾個相關的類型,所以將它們與Handle traits類綁定在一起意味着它們可以全部被聲明爲一個。 –

1

您不能擁有循環依賴關係:Base需要在基礎實例化時未知的Widget value_type。

可能的解決辦法是:傳遞VALUE_TYPE作爲基礎模板參數,使用一個額外的特徵模板,...

template <typename W> 
struct WidgetTraits {}; 

template <typename W> 
class Base 
{ 
    public: 
    typedef typename WidgetTraits<W>::value_type value_type; 
}; 

class Widget; 

template<> 
struct WidgetTraits<Widget> 
{ 
    typedef WidgetHandle value_type; 
}; 

class Widget : public Base<Widget> 
{ 
}; 

另一個(略有不同)實例

template <typename C, typename A> 
class B : public A 
{ 
    public: 
    typedef typename A::value_type value_type; 
}; 


class A 
{ 
    public: 
    typedef WidgetHandle value_type; 
}; 


class C : public B<C, A> 
{ 
}; 
+0

這是真的,但CRTP廣泛使用這種技術嗎? http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern –

+0

好吧,性狀模板聽起來好像可能會更好 - 那會是什麼樣子? –

+0

性狀解決方案更復雜...我會去尋找額外的模板參數。 – Walter