Item 45: 用 member function templates(成员函数模板) 接受 "all compatible types"(“所有兼容类型”)
作者:Scott Meyers
译者:fatalerror99 (iTePub's Nirvana)
发布:http://blog.csdn.net/fatalerror99/
smart pointers(智能指针)是行为很像指针但是增加了指针没有提供的功能的 objects。例如,Item 13 阐述了标准 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
在上面的 smart pointer(智能指针)的示例代码中,每一个语句创建一个新的 smart pointer object(智能指针对象),所以现在我们就集中于我们如何写 smart pointer constructors(智能指针的构造函数),让它以我们想要的方式运转。一个关键的事实是我们无法写出我们需要的全部 constructors(构造函数)。在上面的 hierarchy(继承体系)中,我们能从一个 SmartPtr
class BelowBottom: public Bottom { ... };
我们就需要支持从 SmartPtr
大体上,我们需要的 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
上面的 generalized copy constructor(泛型化拷贝构造函数)没有被声明为 explicit(显式)的。这是故意为之的。built-in pointer types(内建指针类型)之间的类型转换(例如,从派生类指针到基类指针)是隐式的和不需要 cast(强制转型)的,所以让 smart pointers(智能指针)模仿这一行为是合理的。在 templatized constructor(模板化构造函数)中省略 explicit 正好做到这一点。
作为声明,SmartPtr 的 generalized copy constructor(泛型化拷贝构造函数)提供的东西比我们想要的还多。是的,我们需要能够从一个 SmartPtr
假如 SmartPtr 跟随 auto_ptr 和 tr1::shared_ptr 的脚步,提供一个返回被这个 smart pointer(智能指针)持有的 built-in pointer(内建指针)的拷贝的 get member function(get 成员函数)(参见 Item 15),我们可以用 constructor template(构造函数模板)的实现将转换限定在我们想要的范围:
template
class SmartPtr {
public:
template
SmartPtr(const SmartPtr& other) // initialize this held ptr
: heldPtr(other.get()) { ... } // with other's held ptr
T* get() const { return heldPtr; }
...
private: // built-in pointer held
T *heldPtr; // by the SmartPtr
};
我们通过 member initialization list(成员初始化列表),用 SmartPtr 持有的类型为 U* 的指针初始化 SmartPtr
member function templates(成员函数模板)的用途并不限于 constructors(构造函数)。它们的另一个常见的任务是用于支持 assignment(赋值)。例如,TR1 的 shared_ptr(再次参见 Item 13)支持从所有兼容的 built-in pointers(内建指针),tr1::shared_ptrs,auto_ptrs 和 tr1::weak_ptrs(参见 Item 54)构造,以及从除 tr1::weak_ptrs 以外所有这些赋值。这里是从 TR1 规范中摘录出来的一段关于 tr1::shared_ptr 的内容,包括它在声明 template parameters(模板参数)时使用 class 而不是 typename 的偏好。(就像 Item 42 中阐述的,在这里的上下文环境中,它们的含义严格一致。)
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(赋值操作符)的方式没有被声明为 const,于此对照的是 tr1::shared_ptrs 和 tr1::weak_ptrs 的被传送的方式。这是 auto_ptrs 被复制时需要独一无二的被改变的事实的一个必然结果(参见 Item 13)。
member function templates(成员函数模板)是一个极好的东西,但是它们没有改变这个语言的基本规则。Item 5 阐述的编译器可以产生的四个 member functions(成员函数)其中两个是 copy constructor(拷贝构造函数)和 copy assignment operator(拷贝赋值运算符)。tr1::shared_ptr 声明了一个 generalized copy constructor(泛型化拷贝构造函数),而且很明显,当类型 T 和 Y 相同时,generalized copy constructor(泛型化拷贝构造函数)就能被实例化而成为 "normal" copy constructor(“常规”拷贝构造函数)。那么,当一个 tr1::shared_ptr object 从另一个相同类型的 tr1::shared_ptr object 构造时,编译器是为 tr1::shared_ptr 生成一个 copy constructor(拷贝构造函数),还是实例化 generalized copy constructor template(泛型化拷贝构造函数模板)?
就像我说过的,member templates(成员模板)不改变语言规则,而且规则规定如果一个 copy constructor(拷贝构造函数)是必需的而你没有声明,将为你自动生成一个。在一个 class 中声明一个 generalized copy constructor(泛型化拷贝构造函数)(一个 member template(成员模板))不会阻止编译器生成它们自己的 copy constructor(拷贝构造函数)(非模板的),所以如果你要全面支配 copy construction(拷贝构造),你必须既声明一个 generalized copy constructor(泛型化拷贝构造函数)又声明一个 "normal" copy constructor(“常规”拷贝构造函数)。这同样适用于 assignment(赋值)。这是从 tr1::shared_ptr 的定义中摘录的一段,可以作为例子:
template class shared_ptr {
public:
shared_ptr(shared_ptr const& r); // copy constructor
template // generalized
shared_ptr(shared_ptr const& r); // copy constructor
shared_ptr& operator=(shared_ptr const& r); // copy assignment
template // generalized
shared_ptr& operator=(shared_ptr const& r); // copy assignment
...
};
Things to Remember
使用 member function templates(成员函数模板)生成接受所有兼容类型的函数。
如果你为 generalized copy construction(泛型化拷贝构造)或 generalized assignment(泛型化赋值)声明了 member templates(成员模板),你依然需要声明 normal copy constructor(常规拷贝构造函数)和 copy assignment operator(拷贝赋值运算符)。