Effective C++:条款45:运用成员函数模板接受所有兼容类型

(一)

所谓智能指针是“行为像指针”的对象,并提供指针没有的机能。

真实指针做的很好的一件事是。Derived class指针可以隐式转换为base class指针。指向non-const的对象的指针可以转换为指向const对象。

例如:

class Top{...}; 
class Middle: public Top{...}; 
class Bottom: public Middle{...}; 
Top* pt1 = new Middle;        //将Middle*转换成Top* 
Top* pt2 = new Bottom;        //将Bottom*转换成Top* 
const Top* pct2 = pt1;        //将Top*转换成const Top*
但想在用户自定义的智能指针中模拟以上转换,就有点麻烦,我们希望以下代码通过编译:
template<typename T>
class SmartPtr {
public:
	explicit SmartPtr(T* realPtr);
	...
};

SmartPtr<Top> pt1 = SmartPtr<Middle>(new Middle);  //将SmartPtr<Middle>转换为SmartPtr<Top> 
SmartPtr<Top> pt2 = SmartPtr<Bottom>(new Bottom);  //将SmartPtr<Bottom>转换为SmartPtr<Top> 
SmartPtr<const Top> pct2 = pt1;                    //将SmartPtr<Top>转换为SmartPtr<const Top>
但是同一个template的不同具现体之间并不存在什么与生俱来的固有关系(如果带有base-derived关系的B,D两类型分别具现化某个template,产生出的两个具现体并不带有base-derived关系),所以编译器视SmartPtr<Middle>和SmartPtr<Top>为完全不同的classes。为了获得我们希望的SmartPtr class之间的转换能力,我们必须将它们明确地编写出来。


(二)

我们需要的不是写一个构造函数,根据一个SmartPtr<Middle>或一个SmartPtr<Bottom>构造一个SmartPtr<Top>。而是要为它写一个构造模板。这样的模板是member function templates(常简称为member templates),其作用是为class生成函数:

template<typename T>
class SmartPtr {
public:
	template<typename U>   //member template
	SmartPtr(const SmartPtr<U>& other);  //为了生成copy构造函数
};
以上代码的意思是: 任何类型T和任何类型U,可以根据SmarPtr<U>生成一个SmartPtr<T>——因为SmartPtr<T>有个构造函数接受一个SmartPtr<U>参数。这一类构造函数通过对象u创建对象t(例如根据对象SmartPtr<U>创建一个SmartPtr<T>),我们称之为泛化copy构造函数。它并未声明为explicit,那是蓄意的,在模板化构造函数中略去explicit是为了效仿隐式转换。


(三)

完成声明之后,我们必须从某方面对这一member template所创建的成员函数群进行拣选或删除。我们希望根据一个SmartPtr<bottom>创建一个SmartPtr<top>,却不希望根据一个SmartPtr<top>创建一个SmartPtr<bottom>,因为那对public继承而言那是矛盾的;也不希望根据一个SmartPtr<double>创建一个SmartPtr<int>,因为现实中并没有“将double*转换成为int*”的对应隐式转换行为。

代码中约束转换行为,使它符合我们的期望:

template<typename T>
class SmartPtr {
public:	
	template<typename U>
	SmartPtr(const SmartPtr<U>& other) : heldPtr(other.get())
	{}
	T* get() const {return heldPtr}
private:
	T* heldPtr;  //持有的内置(原始)指针
};
成员初值列中,以类型为U*的指针初始化类型为T*成员变量,这个行为只有当“存在某个隐式转化可将一个U*指针转换为一个T*指针”时才能通过编译,而那正是我们想要的。
成员初值列中,以类型为U*的指针初始化类型为T*成员变量,这个行为只有当“存在某个隐式转化可将一个U*指针转换为一个T*指针”时才能通过编译,而那正是我们想要的。SmartPtr<T>有了一个泛化copy构造函数,这个构造函数只在其所获得的实参隶属适当(兼容)类型时才通过编译。


(四)

member function templates并不改变语言的基本规则。条款5说过,编译器可能为我们产生4个成员函数,其中两个是copy构造和copy assignment操作符。现在,shared_ptr声明了一个泛化copy构造函数,而显然一旦类型T和Y相同,泛化copy构造函数会被具现化为“正常的”copy构造函数。那么编译器是会暗自为shared_ptr生成一个copy构造函数呢?还是当某个shared_ptr对象根据另一个同型的shared_ptr对象展开构造行为时,编译器会将“泛化copy构造函数模板”具现化呢?

member templates并不改变语言规则,而语言规则说,如果程序需要一个copy构造函数,你却没声明它,编译器会为你暗自生成一个。声明一个泛化copy构造函数(是个member template)并不阻止编译器生成它们自己的copy构造函数(一个non-template),所以如果你想要控制copy构造函数的方方面面,你必须同时声明泛化copy构造函数和“正常的”copy构造函数。相同规则也适用于赋值操作符。

所以示例如下:

template<class T>
class shared_ptr {
	shared_ptr(shared_ptr const& other);  //copy构造函数
	template<class Y>
	shared_ptr(shared_ptr<Y> const& other);  //泛化copy构造函数
	shared_ptr& operator= (shared_ptr const& other);  //赋值操作符
	template<class Y>
	shared_ptr& operator= (shared_ptr<Y> const& other);  //泛化赋值操作符
};

请记住:

(1)请使用member function templates(成员函数模版)生成“可接受所有兼容类型”的函数。

(2)如果你声明member templates用于“泛化copy构造”或“泛化赋值操作符”,你还是需要声明正常的copy构造函数和赋值操作符。










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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值