上例中由于Stack類模板的聲明中第二個(gè)參數(shù)是一個(gè)類型(typename Container
),所以我們通過Stack<int, std::deque<int>>
定義一個(gè)具體的棧類型時(shí),第二個(gè)參數(shù)傳遞std::deque<int>
,而不能是std::deque
。上述定義中我們一共把int寫了兩遍,而這種重復(fù)是一種必然的重復(fù)。
為了避免上述重復(fù),我們可以讓Stack的第二個(gè)參數(shù)直接是一個(gè)模板,而不再是一個(gè)具體類型。
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類模板的第二個(gè)參數(shù)變?yōu)?code>template<typename> class Container,它的名字仍舊是Container,但是類型變?yōu)橐粋€(gè)模板,這個(gè)模板具有一個(gè)類型參數(shù)。由于Container自身的模板形參名字沒有被使用,所以我們可以省略。按照標(biāo)準(zhǔn)這里聲明Container前的關(guān)鍵字只能是class,不能是typename。最后,模板的模板參數(shù)也可以有默認(rèn)值,這里我們設(shè)置為std::vector
。
有了上面的定義,我們期望可以這樣使用Stack:Stack<int, std::deque> intStack
,但編譯器卻給了我們一個(gè)教訓(xùn)。
std::deque類模板在stl庫中的定義有兩個(gè)類型參數(shù),第一個(gè)參數(shù)是元素類型,第二個(gè)參數(shù)是分配器allocator的類型。雖然std::deque的第二個(gè)類型參數(shù)有默認(rèn)值,但是當(dāng)編譯器使用std::deque替換Container時(shí)卻會(huì)嚴(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)于一個(gè)函數(shù)的入?yún)⑷耘f可以是函數(shù),這是后面我們會(huì)介紹到的高階函數(shù)的概念。