Template Template Parameters(双重模板参数)

一、需求引入

 一个  template parameter 本身也可以是个  class template , 这一点非常有用 。 我们将再次以  stack class template 说明这种用法。 
 
为了使用其它类型的元素容器 , stack class  使用者必须两次指定元素类型: 一次是元素类型本身,另一次是容器类型: 
Stack<int,std::vector<int> > vStack; // int  stack,以vector为容器 
 
如果使用  template template parameter,就可以只指明元素类型,无须再指定容器类型: 
Stack<int,std::vector> vStack; // int  stack,以vector为容器 


二、示例代码

为了实现这种特性,你必须把第二个  template parameter 声明为  template template parameter


原则上程序代码可以写为: 

template <typename T, template <typename ELEM> class CONT = std::deque > 
class Stack { 
private: 
CONT<T> elems; //  元素 


public: 
void push(T const&); // push  元素 
void pop(); // pop 元素 
T top() const; //  传回  stack 顶端元素 

 // stack 是否为空 
bool empty() const {
return elems.empty(); 

}; 
 
与先前的  stack 差别在于,第二个  template parameter 被声明为一个  class template: 
template <typename ELEM> class CONT 


其默认值则由  std::deque<T>  变更为  std::deque。这个参数必须是个  class template,并以第一参数的类型完成实例化: 
CONT<T> elems; 
 
本例「以第一个  template parameter 对第二个  template parameter 进行实例化」只是基于例子本身 的需要。实际运用 时你可以使用   class template  内的任何类型来实例化一个   template template parameter。 
 
和往 常一 样,你也 可以改 用 关键词   class 而不使 用关键 字   typename 来 声明一个  template parameter;但  CONT  定义的是一个  class  类型,因此你必须使用关键词  class  来声明它。
所以,下面的程序代码是正确的:
template <typename T, template <typename ELEM> class CONT = std::deque > //OK 
class Stack { 
... 
}; 
 
下面的程序代码则是错误的: 

//ERROR 
template <typename T, template <typename ELEM> typename CONT = std::deque > 
class Stack {  
... 
}; 
 
由于  template template parameter 中的  template parameter 实际并未用到 , 因此你可以省略其名称 : 
template <typename T, template <typename> class CONT = std::deque > 
class Stack { 
... 
}; 
 
所有成员函数也必须按此原则修改:必须指定其第二个template parameter为template template parameter。同样的规则也适用于成员函数的实作部份。

例如成员函数  push()应该实作如下: 
template <typename T, template <typename> class CONT> 
void Stack<T,CONT>::push (T const& elem) { 
elems.push_back(elem);   //  追加元素 


另请注意,function templates 不允许拥有  template template parameters


三、拓展:Template Template Argument 的 匹配(matching)

如果你试图使用上述新版  Stack,编译器会报告一个错误:默认值  std::deque  不符合 template template parameter CONT  的要求。问题出在  template template argument 不但必须是个  template,而且 其参数 必须严格匹配它 所替换 之   template template parameter  的 参数。 Template  template argument 的默认值不被考虑,因此如果不给出拥有默认值的自变量值时,编译器会认为匹配失败 
 
本例的问题在于:标准库中的  std::deque  template 要求不只一个参数。第二参数是个配置器(allocator),它虽有默认值,但当它被用来匹配CONT  的参数时,其默认值被编译器强行忽略了。 
 
办法还是有的。我们可以重写  class  声明语句,使  CONT  参数要求一个「带两个参数」的容器: 
template <typename T, template <typename ELEM, typename ALLOC = std::allocator<ELEM> > class CONT = std::deque > 
class Stack { 
private: 
CONT<T> elems; //  元素 
... 
}; 
 
由于ALLOC  并未在程序代码中用到,因此你也可以把它省略掉。 


四、最终代码

Stack  template 的最终版本如下。此一版本支持对「不同元素类型」之  stacks  的彼此赋值动作:


#ifndef STACK_HPP 
#define STACK_HPP 
 
#include <deque> 
#include <stdexcept> 
#include <memory> 
 
template <typename T, template <typename ELEM, typename = std::allocator<ELEM> > class CONT = std::deque > 
class Stack { 
private: 
CONT<T> elems; //  元素 


public: 
void push(T const&); // push 元素 
 
void pop(); // pop 元素 
T top() const; //  传回  stack 的顶端元素 

// stack 是否为空 
bool empty() const { 
return elems.empty(); 

 
//  赋予一个「元素类型为  T2」的  stack 
template<typename T2, template<typename ELEM2, typename = std::allocator<ELEM2> > class CONT2
Stack<T,CONT>& operator= (Stack<T2,CONT2> const&); 
}; 
 
template <typename T, template <typename,typename> class CONT> 
void Stack<T,CONT>::push (T const& elem) { 
elems.push_back(elem); //  追加元素 

 
template<typename T, template <typename,typename> class CONT> 
void Stack<T,CONT>::pop () { 
if (elems.empty()) {  
throw std::out_of_range("Stack<>::pop(): empty stack"); 

elems.pop_back(); //  移除最后一个元素 

 
template <typename T, template <typename,typename> class CONT> 
T Stack<T,CONT>::top () const { 
if (elems.empty()) { 
throw std::out_of_range("Stack<>::top(): empty stack"); 

return elems.back(); //  传回最后一个元素的拷贝 

 
 
template <typename T, template <typename,typename> class CONT> 
template <typename T2, template <typename,typename> class CONT2> 
Stack<T,CONT>& 
Stack<T,CONT>::operator= (Stack<T2,CONT2> const& op2) { 

//是否赋值给自己 
if ((void*)this == (void*)&op2) {
return *this;   

 
Stack<T2,CONT2> tmp(op2); //  创建   assigned stack  的一份拷贝 
 elems.clear(); //  移除所有元素 

//复制所有元素 
while (!tmp.empty()) {
elems.push_front(tmp.top()); 
tmp.pop(); 

return *this; 

 
#endif  //STACK_HPP 
 
下面程序使用了上述最终版本的  stack: 
#include <iostream> 
#include <string> 
#include <cstdlib>  
#include <vector> 
#include "stack8.hpp" 
 
int main() { 
try { 
Stack<int> intStack; // stack of ints 
Stack<float> floatStack; // stack of floats 
 
//操控int stack 
intStack.push(42); 
intStack.push(7)


//操控  float stack 
floatStack.push(7.7); 
 
//  赋予一个「不同类型」的  stack 
floatStack = intStack; 
 
//  打印  float stack 
std::cout << floatStack.top() << std::endl; 
floatStack.pop(); 
std::cout << floatStack.top() << std::endl; 
floatStack.pop(); 
std::cout << floatStack.top() << std::endl; 
floatStack.pop(); 


} catch (std::exception const& ex) { 
std::cerr << "Exception: " << ex.what() << std::endl; 

 
// int stack,以  vector  为其内部容器 
Stack<int,std::vector> vStack; 


vStack.push(42); 
vStack.push(7); 
std::cout << vStack.top() << std::endl; 
vStack.pop(); 


}  
 
程序运行的输出结果为: 

42 
Exception: Stack<>::top(): empty stack 

 
注意,template template parameter 是极晚近才加入的C++  特性,因此上面这个程序可作为一个 极佳工具,用来评估你的编译器对  template 特性的支持程度。 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值