Skip to content

Latest commit

 

History

History
61 lines (47 loc) · 2.72 KB

File metadata and controls

61 lines (47 loc) · 2.72 KB

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

如果想在用户定义的智能指针中模拟原始指针的隐式转换,稍稍有点麻烦。我们希望以下代码通过编译:

template <typename T>
class SmartPtr {
public:
  explicit SmartPtr(T* realPtr);
  ...
};

SmartPtr<Top> pt1 = SmartPtr<Middle>(new Middle); // Middle => Top
SmartPtr<Top> pt2 = smartPtr<Bottom>(new Bottom); // Bottom => Top
SmartPtr<const Top> pct2 = pt1;                   // Top => const Top

但是,同一个template的不同实例化之间并不存在什么转换关系。为了获得我们希望的SmartPtr之间的转换能力,我们必须将它们明确地编写出来:

Template和泛型编程(Generic Programming)

现在我们应该关注如何编写智能指针的构造函数,使其行为能够满足我们的转型需要。但是我们根本无法写出所有我们需要的构造函数。一个继承体系可以无限扩充,我们不希望一再修改SmartPtr以满足此类要求。

我们可以为它写一个构造模板,其作用是为class生成很熟:

template <typename T>
class SmartPtr {
public:
  template <typename U>
  SmartPtr(const SmartPtr<U>& other);
  ...
};

但是这个为SmartPtr而些的泛化copy构造函数提供的东西比我们需要的多。它可以对任意类型进行拷贝构造,而我们只希望在继承体系中使用。我们必须从某方面对这一member template所创建的成员函数进行筛选。我们可以在构造模板实现代码中约束转换行为,使他符合我们的期望:

template <typename U>
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* 时才能通过编译。

成员函数模板的效用不限于构造函数,它们常扮演的另一个角色还有赋值操作。

成员函数模板并不改变语言规则,而语言规则说,如果程序需要一个copy构造函数,你却没有声明它,编译器会为你暗自生成一个。在class内声明泛化copy构造函数并不会阻止编译器生成它们自己的copy构造函数,所以如果你想要控制copy构造函数的细节,你必须同时声明泛化copy构造函数和普通copy构造函数。同样的规则也适用于赋值操作。

请记住

  • 请使用成员函数模板生成可接受所有兼容类型的函数。
  • 如果你声明member template用于泛化copy构造或泛化assignment操作,你还是需要声明正常的copy构造函数和copy assignment运算符。