如果想在用户定义的智能指针中模拟原始指针的隐式转换,稍稍有点麻烦。我们希望以下代码通过编译:
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之间的转换能力,我们必须将它们明确地编写出来:
现在我们应该关注如何编写智能指针的构造函数,使其行为能够满足我们的转型需要。但是我们根本无法写出所有我们需要的构造函数。一个继承体系可以无限扩充,我们不希望一再修改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运算符。