模版参数中包含模版
- 允许模板参数本身也是一个类模板,这可能会很有用。我们将继续以stack类模板作为例子
template <class T1, template<class T2>
class Container>
class XCls{
private:
Container<T> c;
public:
...
}
template<class T>
using Lst = List<T,allocator<T>>;
XCls<string, Lst> mylst;
由于Stack类模板的声明中第二个参数是一个类型(typename Container),所以我们通过Stack<int, std::deque
>定义一个具体的栈类型时,第二个参数传递std::deque ,而不能是std::deque。上述定义中我们一共把int写了两遍,而这种重复是一种必然的重复。
为了避免上述重复,我们可以让Stack的第二个参数直接是一个模板,而不再是一个具体类型。
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类模板的第二个参数变为template
class Container,它的名字仍旧是Container,但是类型变为一个模板,这个模板具有一个类型参数。由于Container自身的模板形参名字没有被使用,所以我们可以省略。按照标准这里声明Container前的关键字只能是class,不能是typename。最后,模板的模板参数也可以有默认值,这里我们设置为std::vector。
有了上面的定义,我们期望可以这样使用Stack:Stack<int, std::deque> intStack,但编译器却给了我们一个教训。
std::deque类模板在stl库中的定义有两个类型参数,第一个参数是元素类型,第二个参数是分配器allocator的类型。虽然std::deque的第二个类型参数有默认值,但是当编译器使用std::deque替换Container时却会严格匹配参数,默认值被忽略了。
我们修改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;
};