Effective C++——条款45(第7章)

来源:互联网 发布:成都网络外包公司 编辑:程序博客网 时间:2024/04/30 02:56

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

Use member function templates to accept "all compatible types"

    所谓智能指针是"行为像指针"的对象,并提供指针没有的机能.例如条款13曾提及std::auto_ptr和tr1::shared_ptr如何能够被用来在正确时机自动删除heap-based资源.STL容器的迭代器几乎总是智能指针:无疑不会奢望使用"++"将一个内置指针从linked list的某个节点移到另一个节点,但这在list::iterators身上办得到.
    真实指针做得很好的一件事情是,支持隐式转换(implicit conversions).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 下的不同具现体(instantiation)之间并不存在什么关系,所以编译器视SmartPtr<Middle>和SmartPtr<Top>为完全不同的 class.为了获得希望获得的SmartPtr class 之间的转换能力,必须将它们明确地编写出来.
    Template和泛型编程(Generic Programming)
    在上述智能指针实例中,每一个语句创建了一个新式智能指针对象,所以现在应该关注如何编写智能指针的构造函数,使其行为能够满足转型需要.一个很具关键的观察结果是:永远无法写出需要的所有构造函数.在上述继承体系中,根据一个SmartPtr<Middle>或一个SmartPtr<Bottom>构造出一个SmartPtr<Top>,但如果这个继承体系未来有所扩充,SmartPtr<Top>对象又必须能够根据其他智能指针构造自己.假设添加了:
class BelowBottom : public Bottom { ... };
    因此必须令SmartPtr<BelowBottom>对象得以生成SmartPtr<Top>对象,但是当然不希望一再修改SmartPtr template 以满足此类需求.
    就原理而言,此例中需要的构造函数数量没有止尽,因为一个 template 可被尽量具现化,以至生成无限量函数.因此,似乎需要的不是为SmartPtr写一个构造函数,而是为它写一个构造模板.这样的模板(template)是所谓member function template(常简称为member template),其作用是为 class 生成函数:
template <typename T>class SmartPtr {public:    template <typename U>    SmartPtr(const SmartPtr<U>& other);     // member template,为了生成copy构造函数    ...};
    以上代码的意思是,对任何类型T和任何类型U,这里可以根据SmartPtr<U>生成一个SmartPtr<T>,因为SmartPtr<T>有个构造函数接受一个SmartPtr<U>参数.这一类构造函数根据对象u创建对象t(例如根据SmartPtr<U>创建一个SmartPtr<T>),而u和v的类型是同一个 template 的不同具现体,有时称之为泛化(generalized)copy构造函数.
    上面的泛化copy构造函数并未被声明为 explicit,那是蓄意的,因为原始指针类型之间的转换(例如从derived class 指针转换为base class 指针)是隐式转换,无需明白写出转型动作(cast),所以让智能指针仿效这种行为也是合理的.在模板化构造函数(templatized constructor)中略去 explicit 就是为了这个目的.
    完成声明之后,这个为SmartPtr而写的"泛化copy构造函数"提供的东西比需要的更多.希望根据SmartPtr<Bottom>创建一个SmartPtr<Top>,却不希望根据一个SmartPtr<Top>创建一个SmartPtr<Bottom>,因为那对 public 继承而言(详见条款32)是矛盾的.也不希望根据一个SmartPtr<double>创建一个SmartPtr<int>.因此必须从某方面对这一member template 所创建的成员函数进行挑选或筛除.
    假设SmartPtr遵循 auto_ptr和tr1::shared_ptr所提供的规范,也提供一个get成员函数,返回智能指针对象(详见条款15)所持有的那个原始指针的副本,那么可以在"构造模板"实现代码中的约束转换行为,使它符合期望:
template <typename T>class Smartptr {public:    template <typename U>    SmartPtr(const SmartPtr<U>& other) : heldPtr(other.get())    { ... }     //  以other的heldPtr初始化this的heldPtr    T* get() const { return heldPtr; }private:    T* heldPtr; // 这个SmartPtr持有的内置(原始)指针};
    使用成员初始化列表(member initialization list)来初始化SmartPtr<T>的类型为T*的成员变量,并以类型为U*的指针(由SmartPtr<U>持有)作为初值.这个行为只有的那个"存在某个隐式转换可将一个U*指针转换为一个T*指针"时才能通过编译,而那正是想要的.最终效益是SmartPtr<T>现在有了一个泛化copy构造函数,这个构造函数只在其所获得的实参隶属于适当类型时才通过编译.
    member function template(成员函数模板)的效用不限于构造函数,它们常扮演的另一个角色是支持赋值操作.例如TR1的shared_ptr(详见条款13)支持所有"来自兼容的内置指针,tr1::shared_ptr,auto_ptr和tr1::weak_ptr(详见条款54)"的构造行为,以及所有来自上述各物的赋值操作.
    注意:
    请使用member function template(成员函数模板)生成"可接受所有兼容类型"的函数
    如果声明member template 用于"泛化copy构造"或"泛化assignment操作",还是需要声明正常的copy构造函数和copy assignment操作符.

0 0