2011-07-24 104 views
1

我在教C++自己的模板編程,所以我的一些假設可能是錯誤的 - 請糾正我,如果你看到任何錯誤。專門設計一個包含模板模板的模板

我想使用STL列表作爲函數的模板參數。該函數應該用於各種數據類型,所以我在其原始聲明中將函數定義爲template<class T>而不是template<template<> class T>。我現在想專門支持模板類。

template<class T> 
void function(T param) 
{ 
    // do something with T 
} 

template<template <class T, class Allocator> class listPlaceholder> 
void function(std::list<T, Allocator> param) 
{ 
    // do something with every element in param (I actually need to know it's a list) 
    std::list<T, Allocator>::iterator current = param.begin(); 
    std::list<T, Allocator>::iterator end = param.end(); 

    do { 
    function<T>(*current); 
    } while (++current != end); 
} 

的問題是,當我嘗試編譯此代碼(GCC下),它說,TAllocator未在範圍內定義。我的主要問題是「我如何專注於模板類?」其次,如果可能的話,「我如何提取模板模板參數?」。

如前所述,我正在學習模板編程,因此歡迎使用明顯的解決方案。

回答

3

要聲明的參數

template<template <class T, class Allocator> class listPlaceholder, 
     class T, class Allocator> 
void function(listPlaceholder<T, Allocator> param) 
{ 
    // do something with every element in param (I actually need to know it's a list) 
    typename listPlaceholder<T, Allocator>::iterator current = param.begin(); 
    typename listPlaceholder<T, Allocator>::iterator end = param.end(); 

    do { 
    function<T>(*current); 
    } while (++current != end); 
} 

正式參數列表中使用的名稱沒有意義。實際上你也忘記使用listPlaceholder。但我認爲這是偶然的。

正如另一張海報所說,您還需要typename關鍵字,因爲名稱是dependent names

爲什麼在正式名單的名字是沒有意義的,它比較函數指針:

void f(void (*p)(int t, int allocator), int t, int allocator) { 
    p(t, allocator); 
} 

void g(int a, int b) { 
} 

int main() { 
    f(&g, 0, 1); 
} 

什麼是重要的僅僅是參數的類型,我可以寫void(*p)(int, int)了。在你的情況下,重要的是兩個參數都是類型參數。所以你也可以把模板模板參數也寫成template<class, class> class listPlaceholder,完全等價。

最後但並非最不重要的一點,我想強調的是,您有而不是專門的function,但是您已經用另一個模板重載了它。所以,兩個function s是兩個完全不同的功能模板。

+0

我使用'listPlaceholder'和'std :: list'爲了專門爲'std :: list's,同時保持其他兩個參數模板類的任何特化打開(例如我也使用'std :: pair') 。正如我向其他評論者提及的那樣,額外的模板參數是否意味着您不能在原始函數中使用此修改後的功能。 – fuseinabowl

+0

@fuseinabowl,啊我明白了。那麼,因爲你沒有專精,所以你不需要爲任何更專業化而「保持開放」。無論如何,您無法爲參數化的'std :: list '專門化一個函數模板,因爲這需要部分特化,而功能模板則無法完成。你可以在裏面粘貼'std :: list',稍後爲其他類型添加更多的重載。編譯器將在一次調用中選擇最專業的模板(即最接近參數的模板)。 –

+0

我嘗試了第一個 - 我不知道如何編寫代碼。你能給我一個模板函數聲明的代碼示例嗎? – fuseinabowl

0

使用typename爲:

typename std::list<T, Allocator>::iterator current = param.begin(); 
typename std::list<T, Allocator>::iterator end = param.end(); 

其因iterator依賴名字,所以typename是由編譯器必需的,所以它可以知道iterator實際上是一個型,而不是一個靜態值。

要知道對此進行了詳細,請閱讀此FAQ:

旁邊,你應該寫你的函數模板:

template <class T, class Allocator> 
void function(std::list<T, Allocator> param) 
{ 
    //code.. 
} 
+0

這是真的,但這不是造成OP問題中報告的錯誤的原因。 – templatetypedef

2

g++這裏其實是正確的;您尚未在此範圍內聲明TAllocator。你有模板聲明

template<template <class T, class Allocator> class listPlaceholder> 
    void function(std::list<T, Allocator> param) 

說:「我參數化了一個類模板,它接受兩個類作爲參數。但是,這些參數的名稱無法在模板正文的任何​​位置訪問。他們大多是有作爲佔位符,和上面的模板聲明等效於

template<template <class, class> class listPlaceholder> 
    void function(std::list<T, Allocator> param) 

這類似於如何,如果你要宣佈了另一個函數作爲參數常規C++函數,你不能訪問參數的名稱。例如,這是非法的:

void DoSomething(void function(int x, int y)) { 
    x = 5; // Error! 
} 

因爲它是相當於

void DoSomething(void function(int, int)) { 
    x = 5; // Error! 
} 

我相信你想要做的是改變你的模板函數簽名看起來像這樣的內容:

template<class T, class Allocator> 
    void function(std::list<T, Allocator> param) 

這就是說:「這個函數是通過兩種類型參數化的,當作爲參數提供一個參數化的類型和一個分配器時,這個函數的主體可以參考第其他類型爲TAllocator。「

+1

謝謝,這很好地解釋了這個問題。這是否意味着我將不必重新聲明原始函數,而是使用這些額外的模板參數,然後將2模板函數專門化到列表中?你將如何去實現與1模板類相同的結果,而不會與原始函數混淆? – fuseinabowl

+0

如果你想擁有一個通用的「列表以外的東西」函數和一個更專用的「僅用於列表」函數,那麼你會寫第一個版本作爲模板函數,它接受某種類型的「T」,第二個函數爲以上。在重載解析期間,C++將始終選擇更專用的函數。 – templatetypedef

+0

謝謝你的幫助,這非常有用:) – fuseinabowl