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

来源:互联网 发布:云计算 erp 标题 编辑:程序博客网 时间:2024/05/01 18:05

(一)

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

真实指针做的很好的一件事是。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 templateSmartPtr(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
原创粉丝点击