1、概念
模板的模板参数(template template parameters),这个名字有点长,首先要理解什么是模板的模板参数。需要知道的是函数模板是不支持模板的模板参数的,所以这儿所指的模板的模板参数就是类模板的模板参数,即对一个类模板,它的模板参数本身也是一个模板,更确切地说是一个类模板。
2、例子
为了说明怎样使用模板的模板参数,以STL中的容器适配器stack为例较为恰当。下面是SGI-STL stack的部分源码:
template<typename T, typename Sequence = deque<T> >
class stack
{
protected:
Sequence c;
//…
};
从源码可以看出来,stack的默认容器是deque,一般可以满足用户的需求,用户也可以另外指定容器。如指定vector作为stack的底层容器, 定义如下:
stack<int,std::vector<int> > s; //别忘了”#include<stack>”和 “#include <vector>”
stack类模板的第二个模板参数是一个类型,在这儿我们指定为vector<int>,从stack的定义我们可以知道,程序员如果要替换缺省的内部容器的话,必须两次指定元素的类型,如上面两次指定了int。然而,如果我们使用模板的模板参数,就可以只指定容器的类型而不需要指定所含元素的类型,于是我们可以这样创建一个stack对象,如下:
stack<int, std::vector> s;
为了获得这个特性,我们需要把stack类模板的第二个模板参数指定为模板的模板参数(呵呵,我们在修改SGI-STL源码),如下:
template<typename T,
template <typename E> class Sequence = std::deque >
class stack
{
protected:
Sequence<T> c;
//…
};
我们把第2个模板参数声明为了一个类模板:
template<typename E> class Sequence = std::deque
缺省值也从std::deque<T>变成了std::deuqe,第二个参数必须是一个类模板,并由第一个模板参数传递进来的类型进行实例化。于是我们可以如下创建一个stack对象:
stack<int, std::vector> s;
如果我们在编译器上编译,会得到错误信息:缺省值std::deque和模板的模板参数Sequence不匹配。其实如是你熟悉STL,应该知道问题出在哪儿。对于STL的容器vector,deque, list, map, set, multimap, multiset,它们都有一个缺省的模板参数,它便是空间配置器allocator,它负责容器对空间的配置与管理。配置器实现了动态空间配置,空间管理,空间释放,它本质上是一个类模板(class template)。SGI-STL的空间配置器实现相对比较复杂,内容比较多,也不是本篇的主题,有兴趣的网友可以参考侯捷的STL源码剖析。下面给出SGI-STL allocator和deque的类模板声明,如下:
template<typename _Tp>
class allocator; template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class deque;
从上面的声明可以看出deuqe和allocator只是一个类模板,而allocator是类模板deque的第二个模板参数,是缺省的空间配置器(用户也可以定义自己的空间配置器)。现在我们再来分析错误原因。
对于stack类模板声明:
template<typename T,
template <typename E> class Sequence = std::deque>
class stack;
它的第二个模板参数template <typename E> class Sequence = std::deque也是一个类模板,它只有一个模板参数 ,即template<typename E>中的E,但对std::deque却有两个模板参数(见上面的声明),故错误信息会提示缺省值std::deque和模板的模板参数Sequence不匹配。于是我们可以如下重新声明stack类,如下:
template<typename T,
template<typename E, typename Alloc = allocator<T> >
class Sequence = std::deque>
class stack
{
protected:
Sequence<T> c;
//…
}
由于我们在stack类的实现中并没有用到模板参数E和Alloc, 所以我们可以将其省略。如下:
template<typenameT,
template<typename , typename = allocator<T> >
class Sequence = std::deque> class stack;
3、注意事项:
(1)由于STL版本比较多,故实现也存在差异,如空间配置器就有所不同,所以上面代码不具有可移植性。
(2)模板的模板实参要求与模板的模板参数精确匹配。
(3)函数模板不具有模板的模板参数
4、更多参考资料
[1]侯捷著.STL源码剖析[M],湖北华中科技大学出版社.2011.12
[2][美]David Vandevoorde,Nicolai M.Josuttis著.陈伟柱译.C++ Templates.北京.人民邮电出版社
[3][美]Nicolai M.Josuttis著.侯捷,孟岩译.C++标准程序库.湖北.华中科技大学出版社

本文介绍了C++中的模板模板参数,重点讲述了如何在STL的stack类模板中使用这一特性。通过示例展示了如何仅指定容器类型而不指定元素类型,讨论了与STL容器的默认模板参数匹配的问题,以及在不同STL版本中的不兼容性。同时提到了模板模板参数的匹配要求和在函数模板中的不可用性。
946

被折叠的 条评论
为什么被折叠?



