template 在 C 裡面,算是一個很重要、也很實用的概念。它可以用同樣的程式、來處理不同類型的資料,大幅簡化程式碼的重複性。
而這一篇,算是來簡單紀錄一下,所謂的「template of template」的玩法吧~主要的參考資料,是《Templates of templates》這篇文章。也請注意,這篇只是 Heresy 自己簡單的紀錄,寫得不算很嚴謹。
首先,一般的 template 寫法,可能會像是下面這樣:
class CData
{
public:
std::vector<DType> mContent;
};
在上面的類別 CData 裡,是用一個 std::vector 來儲存資料,而裡面的型別則是 DType;使用的時候,基本上會是:
但是,如果希望可以讓使用者決定要用哪種 STL 容器(container)時該怎麼辦呢?比較直覺的方法,就是寫成:
class CData
{
public:
TContainer<DType> mContent;
};
然後在要使用的時候,寫成:
也就是把 std::vector 的部分,也用 template 的方法來寫。但是很遺憾,這樣的語法是有問題的。
而比較簡單的修改,應該是改成:
class CData
{
public:
TContainer mContent;
};
這樣寫的話,使用時則是會像下面這樣:
但是這樣的缺點,就是把資料型別和容器的型別綁在一起了,其實有的時候可能不是那麼地實用。
而如果要把資料型別和容器的類別切割開的話,則可以寫成:
template<typename T> class TContainer >
class CData
{
public:
TContainer<DType> mContent;
};
在這邊,就是很明確地告訴編譯器,TContainer 是一個 template 的類別,而他有一個 template 的參數。這樣一來,就可以任意地套用符合形式的容器了~
不過,如果是要使用 STL 的 vector 的話,由於他實際上有兩個 template 參數(參考),所以直接拿來用會因為 template 參數不相同、而有編譯階段的錯誤;如果要用的話,則是需要再封包一次才行,封包方法大致上可以寫成:
class CVector : public std::vector<T>
{};
實際使用時是:
而對於其他 STL 的容器、或是自己定義的資料類別,基本上也都是可以用類似的方法來操作的。
為什麼會跑來看這個?其實 Heresy 主要是把這個用在 callable object 上,也就是想用 template 來展開函式。簡單講,就是想下類似下面架構的程式:
template<template<typename T> class TFunc>
void FuncTemp(int iMode)
{
if (iMode == 0 )
{
TFunc<int>()();
}
else if ( iMode == 1 )
{
TFunc<float>()();
}
}
template<typename T>
class CFunc1
{
public:
void operator()(){
}
};
template<typename T>
class CFunc2
{
public:
void operator()(){
}
};
int main()
{
FuncTemp<CFunc1>(1);
FuncTemp<CFunc2>(0);
}
本來 CFunc1 和 CFunc2 都不是想寫成 class、而是想寫成 template function,但是那樣寫是不合法的,所以只能寫成類別。而後來也因為這樣寫太繁瑣了,還是放棄了…