上例中由于Stack類模板的聲明中第二個參數(shù)是一個類型(typename Container
)逻翁,所以我們通過Stack<int, std::deque<int>>
定義一個具體的棧類型時,第二個參數(shù)傳遞std::deque<int>
胳泉,而不能是std::deque
。上述定義中我們一共把int寫了兩遍岩遗,而這種重復(fù)是一種必然的重復(fù)扇商。
為了避免上述重復(fù),我們可以讓Stack的第二個參數(shù)直接是一個模板宿礁,而不再是一個具體類型案铺。
template<typename T,
template<typename> class Container = std::vector>
struct Stack
{
void push(const T& elem)
{
elems.push_back(elem);
}
T pop()
{
if(empty()) throw std::out_of_range("Stack<>::pop: empty!");
auto elem = elems.back();
elems.pop_back();
return elem;
}
bool empty() const
{
return elems.empty();
}
private:
Container<T> elems;
};
如上Stack類模板的第二個參數(shù)變?yōu)?code>template<typename> class Container,它的名字仍舊是Container窘拯,但是類型變?yōu)橐粋€模板红且,這個模板具有一個類型參數(shù)坝茎。由于Container自身的模板形參名字沒有被使用涤姊,所以我們可以省略。按照標(biāo)準(zhǔn)這里聲明Container前的關(guān)鍵字只能是class嗤放,不能是typename思喊。最后,模板的模板參數(shù)也可以有默認(rèn)值次酌,這里我們設(shè)置為std::vector
恨课。
有了上面的定義舆乔,我們期望可以這樣使用Stack:Stack<int, std::deque> intStack
,但編譯器卻給了我們一個教訓(xùn)剂公。
std::deque類模板在stl庫中的定義有兩個類型參數(shù)希俩,第一個參數(shù)是元素類型,第二個參數(shù)是分配器allocator的類型纲辽。雖然std::deque的第二個類型參數(shù)有默認(rèn)值颜武,但是當(dāng)編譯器使用std::deque替換Container時卻會嚴(yán)格匹配參數(shù),默認(rèn)值被忽略了拖吼。
我們修改Stack的定義如下:
template<typename T,
template<typename Elem, typename Allocator = std::allocator<Elem>> class Container = std::vector>
struct Stack
{
void push(const T& elem)
{
elems.push_back(elem);
}
T pop()
{
if(empty()) throw std::out_of_range("Stack<>::pop: empty!");
auto elem = elems.back();
elems.pop_back();
return elem;
}
bool empty() const
{
return elems.empty();
}
private:
Container<T> elems;
};
現(xiàn)在Stack<int, std::deque> intStack
可以編譯通過了鳞上!
可以看到模板的模板參數(shù)特性,可以讓類模板之間通過模板參數(shù)互相組合吊档。如果我們將類模板比作C++編譯期的函數(shù)篙议,那么可以接受模板作為參數(shù)的類模板,就相當(dāng)于一個函數(shù)的入?yún)⑷耘f可以是函數(shù)怠硼,這是后面我們會介紹到的高階函數(shù)的概念鬼贱。