Member Templates(成 员模板 )

一、问题引入

在前面博客提到的stacks模板类,通常只有当两个  stacks  类型相同,也就是当两个stacks  拥有相同类型的元素时,你才能对它们相互赋值(assign),也就是将某个 stack  整体赋值给另一个。你不能把某种类型的  stack赋值给另一种类型的 stack,即使这两种类型之间可以隐式转型:

 
Stack<int> intStack1, intStack2; // stacks for ints 
Stack<float> floatStack; // stack for floats 
... 
intStack1 = intStack2; // OK:两个  stacks 拥有相同类型 
floatStack = intStack1; // ERROR:两个  stacks 类型不同 
default  assignment 运算符要求左右两边拥有相同类型,而以上情况中,拥有不同类型元素的两 个  stacks,其类型并不相同。 


二、解决思路

然而,只要把   assignment  运算符定义为一个   template,你就可以让两个「类型不同,但其元素  可隐式转型」的  stacks 互相赋值。

为完成此事,Stack<>  需要这样的声明: 
 
template <typename T> 
class Stack { 
private: 
std::deque<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> 
Stack<T>& operator= (Stack<T2> const&); 

}; 
  
新增加的  assignment 运算符实作如下: 
template <typename T> 
template <typename T2>  
Stack<T>& Stack<T>::operator= (Stack<T2> const& op2) { 

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

 
Stack<T2> tmp(op2); //  建立  op2  的一份拷贝 
elems.clear(); //  移除所有现有元素 

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

return *this; 


三、使用

有了这个  member template,你就可以把一个  int  stack 赋值(assign)给一个  float stack: 
 
Stack<int> intStack1, intStack2; //stack for ints 
Stack<float> floatStack; //stack for floats 
... 
floatStack = intStack1; // OK:两个  stacks 类型不同,但  int  可转型为  float。 
 
当然,这个赋值动作并不会改动  stack和其元素的类型。赋值完成后,floatStack  的元素类型还是  float,而  top()仍然传回  float  值。


也许你会认为,这么做会使得类型检查失效,因为你甚至可以把任意类型的元素赋值给另一个 stack。然而事实并非如此。必要的类型检查会在「来源端   stack」的元素(拷贝)被安插到「目  的端  stack」时进行:

elems.push_front(tmp.top()); 

 
如 果 你将一个   string stack   赋值 给一个   float stack ,以 上 述 句 编译时就会发生
错 误: 「elems.push_front()无法接受  tmp.top()的返回类型」。具体的错误讯息因编译器而异,但  含义类似。 
 
Stack<std::string> stringStack; // stack of strings 
Stack<float> floatStack; // stack of floats 
… 
floatStack = stringStack; //  错误:std::string  无法转型为  float 
 
注意,前述的  template assignment 运算符并不取代  default  assignment 运算符。如果你在相同  类型的  stack 之间赋值,编译器还是会采用  default assignment 运算符。 


四、拓展

和先前一样,你可以把内部容器的类型也参数化: 

template <typename T, typename CONT = std::deque<T> > 
class Stack { 
private: 
CONT 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, typename CONT2> 
Stack<T,CONT>& operator= (Stack<T2,CONT2> const&); 

}; 
 
此时的  template assignment 运算符可实作如下: 


template <typename T, typename CONT> 
template <typename T2, typename 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); //  创建  op2  的一份拷贝 
elems.clear(); //  移除所有现有元素 

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

return *this; 

 
记住,对  class templates 而言,只有「实际被调用的成员函数」才会被实例化。因此如果你不至  于令不同 (元素) 类型的  stacks  彼此赋值, 那么甚至可以拿  vector 当作内部元素的容器 (注:  而先前的程序代码完全不必改动): 
// stack for ints,使用  vector  作为内部容器 
Stack<int,std::vector<int> > vStack; 
... 
vStack.push(42); 
vStack.push(7); 
std::cout << vStack.top() << std::endl; 
由于   template assignment  运算符并未被用到,编译器不会产生任何错误讯息抱怨说「内部容器  无法支持  push_front()操作」。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值