C++箴言:用成员函数模板接受兼容类型
smart pointers(智能指针)是行为很像指针但是增加了指针没有提供的功能的 objects。例如,《C++箴言:使用对象管理资源》阐述了标准 auto_ptr 和 tr1::shared_ptr 是怎样被应用于在恰当的时间自动删除的 heap-based resources(基于堆的资源)的。STL containers 内的 iterators(迭代器)几乎始终是 smart pointers(智能指针);你绝对不能指望用 "++" 将一个 built-in pointer(内建指针)从一个 linked list(线性链表)的一个节点移动到下一个,但是 list::iterators 可以做到。
real pointers(真正的指针)做得很好的一件事是支持 implicit conversions(隐式转换)。derived class pointers(派生类指针)隐式转换到 base class pointers(基类指针),pointers to non-const objects(指向非常量对象的指针)转换到 pointers to const objects(指向常量对象的指针),等等。例如,考虑在一个 three-level hierarchy(三层继承体系)中能发生的一些转换:
class Top { ... };
class Middle: public Top { ... };
class Bottom: public Middle { ... };
Top *pt1 = new Middle; // convert Middle* => Top*
Top *pt2 = new Bottom; // convert Bottom* => Top*
const Top *pct2 = pt1; // convert Top* => const Top*
在 user-defined smart pointer classes(用户定义智能指针类)中模仿这些转换是需要技巧的。我们要让下面的代码能够编译:
template
class SmartPtr {
public: // smart pointers are typically
explicit SmartPtr(T *realPtr); // initialized by built-in pointers
...
};
SmartPtr pt1 = // convert SmartPtr =>
SmartPtr (new Middle); // SmartPtr
SmartPtr pt2 = // convert SmartPtr =>
SmartPtr (new Bottom); // SmartPtr
SmartPtr pct2 = pt1; // convert SmartPtr =>
// SmartPtr
在同一个 template(模板)的不同 instantiations(实例化)之间没有 inherent relationship(继承关系),所以编译器认为 SmartPtr 和 SmartPtr 是完全不同的 classes,并不比(比方说)vector 和 Widget 的关系更近。为了得到我们想要的在 SmartPtr classes 之间的转换,我们必须显式地为它们编程。
在上面的 smart pointer(智能指针)的示例代码中,每一个语句创建一个新的 smart pointer object(智能指针对象),所以现在我们就集中于我们如何写 smart pointer constructors(智能指针的构造函数),让它以我们想要的方式运转。一个关键的事实是我们无法写出我们需要的全部 constructors(构造函数)。在上面的 hierarchy(继承体系)中,我们能从一个 SmartPtr 或一个 SmartPtr 构造出一个 SmartPtr ,但是如果将来这个 hierarchy(继承体系)被扩充,SmartPtr objects 还必须能从其它 smart pointer types(智能指针类型)构造出来。例如,如果我们后来加入
class BelowBottom: public Bottom { ... };
我们就需要支持从 SmartPtr objects 到 SmartPtr objects 的创建,而且我们当然不希望为了做到这一点而必须改变 SmartPtr template。
大体上,我们需要的 constructors(构造函数)的数量是无限的。因为一个 template(模板)能被实例化而产生无数个函数,所以好像我们不需要为 SmartPtr 提供一个 constructor function(构造函数函数),我们需要一个 constructor template(构造函数模板)。这样的 templates(模板)是 member function templates(成员函数模板)(常常被恰如其分地称为 member templates(成员模板))——生成一个 class 的 member functions(成员函数)的 templates(模板)的范例:
template
class SmartPtr {
public:
template // member template
SmartPtr(const SmartPtr & other); // for a "generalized
... // copy constructor"
};
这就是说对于每一种类型 T 和每一种类型 U,都能从一个 SmartPtr 创建出一个 SmartPtr ,因为 SmartPtr 有一个取得一个 SmartPtr 参数的 constructor(构造函数)。像这样的 constructor(构造函数)——从一个类型是同一个 template(模板)的不同实例化的 object 创建另一个 object 的 constructor(构造函数)(例如,从一个 SmartPtr 创建一个 SmartPtr )——有时被称为 generalized copy constructors(泛型化拷贝构造函数)。
上面的 generalized copy constructor(泛型化拷贝构造函数)没有被声明为 explicit(显式)的。这是故意为之的。built-in pointer types(内建指针类型)之间的类型转换(例如,从派生类指针到基类指针)是隐式的和不需要 cast(强制转型)的,所以让 smart pointers(智能指针)模仿这一行为是合理的。在 templatized constructor(模板化构造函数)中省略 explicit 正好做到这一点。
作为声明,SmartPtr 的 generalized copy constructor(泛型化拷贝构造函数)提供的东西比我们想要的还多。是的,我们需要能够从一个 SmartPtr 创建一个 SmartPtr ,但是我们不需要能够从一个 SmartPtr 创建一个 SmartPtr ,这就像颠倒 public inheritance(公有继承)的含义(参见《 C++箴言:确保公开继承模拟“is-a” 》)。我们也不需要能够从一个 SmartPtr 创建一个 SmartPtr ,因为这和从 int* 到 double* 的 implicit conversion(隐式转换)是不相称的。我们必须设法过滤从这个 member template(成员模板)生成的 member functions(成员函数)的群体。
假如 SmartPtr 跟随 auto_ptr 和 tr1::shared_ptr 的脚步,提供一个返回被这个 smart pointer(智能指针)持有的 built-in pointer(内建指针)的拷贝的 get member function(get 成员函数)(参见《 C++箴言:在资源管理类中准备访问裸资源 》),我们可以用 constructor template(构造函数模板)的实现将转换限定在我们想要的范围:
template
class SmartPtr {
public:
template
SmartPtr(const SmartPtr & other) // initialize this held ptr
: heldPtr(other.get()) { ... } // with others held ptr
T* get() const { return heldPtr; }
...
private: // built-in pointer held
T *heldPtr; // by the SmartPtr
};
我们通过 member initialization list(成员初始化列表),用 SmartPtr 持有的类型为 U* 的指针初始化 SmartPtr 的类型为 T* 的 data member(数据成员)。这只有在“存在一个从一个 U* 指针到一个 T* 指针的 implicit conversion(隐式转换)”的条件下才能编译,而这正是我们想要的。最终的效果就是 SmartPtr 现在有一个 generalized copy constructor(泛型化拷贝构造函数),它只有在传入一个 compatible type(兼容类型)的参数时才能编译。
member function templates(成员函数模板)的用途并不限于 constructors(构造函数)。它们的另一个常见的任务是用于支持 assignment(赋值)。例如,TR1 的 shared_ptr(再次参见《 C++箴言:使用对象管理资源 》)支持从所有兼容的 built-in pointers(内建指针),tr1::shared_ptrs,auto_ptrs 和 tr1::weak_ptrs构造,以及从除 tr1::weak_ptrs 以外所有这些赋值。这里是从 TR1 规范中摘录出来的一段关于 tr1::shared_ptr 的内容,包括它在声明 template parameters(模板参数)时使用 class 而不是 typename 的偏好。(就像《 C++箴言:理解typename的两个含义 》中阐述的,在这里的上下文环境中,它们的含义严格一致。)
template class shared_ptr {
public:
template // construct from
explicit shared_ptr(Y * p); // any compatible
template // built-in pointer,
shared_ptr(shared_ptr const& r); // shared_ptr,
template // weak_ptr, or
explicit shared_ptr(weak_ptr const& r); // auto_ptr
template
explicit shared_ptr(auto_ptr & r);
template // assign from
shared_ptr& operator=(shared_ptr const& r); // any compatible
template // shared_ptr or
shared_ptr& operator=(auto_ptr & r); // auto_ptr
...
};
除了 generalized copy constructor(泛型化拷贝构造函数),所有这些 constructors(构造函数)都是 explicit(显式)的。这就意味着从 shared_ptr 的一种类型到另一种的 implicit conversion(隐式转换)是被允许的,但是从一个 built-in pointer(内建指针)或其 smart pointer type(智能指针类型)的 implicit conversion(隐式转换)是不被许可的。(explicit conversion(显式转换)——例如,经由一个 cast(强制转型)——还是可以的。)同样引起注意的是 auto_ptrs 被传送给 tr1::shared_ptr 的 constructors(构造函数)和 assignment operators(
补充:软件开发 , C++ ,