Template Template Parameters(双重模板参数)

来源:互联网 发布:淘宝小白帽 编辑:程序博客网 时间:2024/05/18 23:15

一、需求引入

 一个  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 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 年检标志跟车牌号不一样怎么办 纹完身后喝酒了怎么办 孩子学习态度不端正怎么办 孩子高考三模考差了家长怎么办 一年级学生学习马虎大意怎么办 一年级下册孩子做作业马虎怎么办 一年级孩子做题马虎怎么办 四年级小孩不喜欢数学该怎么办 工作出了大错误怎么办 遇到不讲道理的人怎么办 四岁宝宝爱生闷气怎么办 一岁宝宝没耐心怎么办 孩子挑食 幼儿园老师该怎么办 小孩被惯的无法无天怎么办 高中的儿子不写作业怎么办 儿子总是不写作业怎么办 长鸡眼脚背肿了怎么办 脚上反复长鸡眼怎么办 脚底长鸡眼很痒怎么办 小脚趾上长鸡眼怎么办 6岁宝宝不爱学习怎么办 上班站久了腿肿怎么办 站时间长了腿肿怎么办 孩子做作业老是粗心大意怎么办 高中孩子没学习兴趣怎么办 初三孩子失去学习兴趣怎么办 初二对学习兴趣不大怎么办 脸上痒发红发肿怎么办 孩子作业拖拉爱丢三落四怎么办 腿肌肉按摩肿了怎么办 孩子上一年级成绩差怎么办 小孩脖子拧筋了怎么办 小孩塑料玩具拧不出来怎么办 一年级孩子做数学题粗心怎么办 手和脚有点肿怎么办 手破了之后肿了怎么办 手指肿了有脓怎么办 宝宝手指红肿有脓怎么办 孩子一听做作业就烦气怎么办 虎皮鹦鹉脚瘸了怎么办 虎皮鹦鹉脚受伤了怎么办