2016-01-17 23 views
2

我在我的C++項目中使用了模板,並且在使用模板類型作爲模板模板參數時遇到問題。我想描述的最好辦法是給產生錯誤的例子:使用模板類作爲模板模板參數在成員變量中時出錯

template <template<class> class P, typename T> 
class Foo { 
    P<T> baz; 
}; 

template <class T> 
class Bar { 
    Foo<Bar, T> memberFoo; 

    void makeFoo() { 
     Foo<Bar, T>* f = new Foo<Bar, T>(); 
    } 
}; 

Foo<Bar, int> globalFoo; 

globalFoo原因沒有錯誤的聲明,但memberFoo的聲明和f導致編譯錯誤:

error: template argument for template template parameter must be a class template or type alias template

只有在Bar類的聲明中使用Bar作爲模板參數時纔會出現該錯誤,但是同時使用clang和g ++。這看起來像某些地方會記錄的東西,但使用Google搜索不會產生SO問題或其他文檔。

模板的這種用法在C++中是不合法的,還是我誤解了如何定義和使用模板?如果這種設計架構不被C++ 11標準所允許,我可以使用什麼解決方法?

+2

我認爲問題是,定義中'Bar',原名'Bar'指*此*專業化 - 那就是'酒吧' - 而不是模板作爲一個整體。一種解決方法:添加使用BAR2 =酒吧'模板;在''的定義Bar'頂部,並使用'Bar2'到位Bar'的'你需要參考非特模板。 –

+0

這似乎是遞歸的。 Bar有一個Foo成員,但是Foo是模板化的,因此它具有與它所屬的類完全相同的Bar成員。因此,酒吧包含自己,這是不允許的。特定的錯誤是由於在定義期間嘗試使用Bar作爲模板模板參數導致的,我認爲沒有前向聲明。 –

回答

2

問題是在實例化時會產生不完整的類型。 Clang和G ++會產生不同的錯誤,因爲Clang(顯然)沒有實現C++ 11規則,即在用作模板模板參數時,注入的類名可以引用類模板本身(Igor的建議不起作用)。將Bar更改爲::Bar修復了此錯誤,這使得Clang指出了像G ++那樣的不完整錯誤。將baz更改爲P<T>*可以編譯。

N.B.儘管它編譯,它可能是未定義的行爲。我建議重新設計你的課程。

0

基於@Igor的評論,我已經找到了一些針對這個問題的解決方法。基於參考Bar在其聲明內的事實是指Bar(引用@Igor)。

請注意,所有這些解決方法都依賴於baz可以聲明爲指針這一事實。 baz不是一個指針導致這是在評論中提到的@Nir並導致該錯誤的遞歸問題:

field has incomplete type 'Foo<Bar<int>, int>'

解決方法1

添加的Bar預先聲明,並創建一個模板別名Bar2。定義memberFoof

template <template <typename> class P, typename T> 
class Foo { 
    P<T>* baz; 
}; 

template<typename T> class Bar; 
template <typename U> using Bar2 = Bar<U>; 

template <class T> 
class Bar { 
    Foo<Bar2, T> memberFoo; 

    void makeFoo() { 
     Foo<Bar2, T>* f = new Foo<Bar2, T>(); 
    } 
}; 

Foo<Bar, int> globalFoo; 

向前聲明和使用模板別名迫使編譯器使用的Bar的非特化版本,而它默認使用非專業化:對於這個編譯,baz必須是指針版本的定義爲globalFoo

解決方法2

此解決方案是基於@ user5800314的答案。我覺得沒有必要重新陳述他的解決方法,但我確實認爲值得注意的是它的工作原因。

我讀過關於注入類名和C++ 11的similar SO question,但是這裏最重要的區別是我的代碼不能在g ++上編譯,而他們的代碼卻沒有。我不認爲這個問題是缺少注入類名的實現。我相信這個解決方法,因爲使用::Bar代替Bar再次強制編譯器來訪問,而不是訪問的Bar本地(專業)版的Bar全球(非特)版本修復了編譯錯誤。

解決方法3

指定Foo爲具有類(或類型名稱)模板參數,而不是一個模板的模板參數,並且是明確的關於哪個專業化每當使用Foo模板被使用。這也要求baz是一個指針,並且,它不使用模板類型:通過要求一個特定的類提供給Foo模板

template <class P, typename T> 
class Foo { 
    P* baz; 
}; 

template <class T> 
class Bar { 
    Foo<Bar, T> memberFoo; 

    void makeFoo() { 
     Foo<Bar, T>* f = new Foo<Bar, T>(); 
    } 
}; 

Foo<Bar<int>, int> globalFoo; 

這種解決方法解決了的模板的模板參數潛在的混亂的。此解決方法在某些情況下可能無法使用,但在其他情況下可能是一個優雅的解決方案。以我的情況爲例,我不需要從Bar以外實例化Foo的實例,所以這是解決編譯錯誤的一個非常好的方法。

P.S.我真的想貸記@ user5800314,因爲他的變通辦法工作,但是我在這裏提供一個不同的解釋,因爲我在這裏提供的解釋是什麼,我相信是正確的,我不覺得我可以標記@ user5800314的答案已被接受。

+0

你發佈的一些解決方法實際上很奇怪。我認爲這是一種X-Y情況,你可能最好告訴人們你想做什麼。我的意思是,你想寫一個遞歸數據結構嗎?這是不尋常的,而且相對很少有一個好主意(在C++中)讓數據結構指向其他類的實例。 –

+0

我只是分享了我發現使這個班級結構起作用的東西。我在各種模塊之間傳遞數據(這裏用'Bar'類表示,他們必須知道他們連接了哪些模塊,連接是用'Foo'類描述的。是否有更好的方式來描述模塊和他們的連接使用C++? – KFox

相關問題