C ++协变模板

时间:2009-03-12 15:46:27

标签: c++ templates covariance

我觉得之前曾经问过这个问题,但我无法在SO上找到它,也无法在Google上找到任何有用的信息。也许“covariant”不是我正在寻找的词,但这个概念与函数的协变返回类型非常相似,所以我认为它可能是正确的。这是我想要做的,它给了我一个编译器错误:

class Base;
class Derived : public Base;

SmartPtr<Derived> d = new Derived;
SmartPtr<Base> b = d; // compiler error

假设这些课程充分充实......我想你明白了。由于某些不明原因,它无法将SmartPtr<Derived>转换为SmartPtr<Base>。我记得在C ++和许多其他语言中这是正常的,虽然目前我不记得为什么。

我的根本问题是:执行此赋值操作的最佳方法是什么?目前,我将指针从SmartPtr中拉出来,明确地将其向上转换为基类型,然后将其包装在适当类型的新SmartPtr中(请注意,这不会泄漏资源,因为我们的自制的SmartPtr类使用侵入式引用计数。那是漫长而混乱的,特别是当我需要将SmartPtr包装在另一个对象中时...任何快捷方式?

6 个答案:

答案 0 :(得分:15)

SmartPtr<Base>SmartPtr<Derived>SmartPtr模板的两个不同实例。这些新类不共享BaseDerived所做的继承。因此,你的问题。

  

执行此分配操作的最佳方法是什么?

 SmartPtr<Base> b = d; 

不调用赋值运算符。这会调用copy-ctor(大多数情况下都会删除副本),就像你写的一样:

 SmartPtr<Base> b(d); 

提供一个SmartPtr<OtherType>并实现它的复制文件。赋值运算符也是如此。你必须写出copy-ctor和op =记住SmartPtr的语义。

答案 1 :(得分:11)

复制构造函数和赋值运算符都应该能够采用不同类型的SmartPtr并尝试将指针从一个复制到另一个。如果类型不兼容,编译器会抱怨,如果它们兼容,你就解决了问题。像这样:

template<class Type> class SmartPtr
{
    ....
    template<class OtherType> SmartPtr(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> copy constructor

    template<class OtherType> SmartPtr<Type> &operator=(const SmartPtr<OtherType> &blah) // same logic as the SmartPtr<Type> assignment operator
};

答案 2 :(得分:5)

模板不协变,这很好;想象在下列情况下会发生什么:

vector<Apple*> va;
va.push_back(new Apple);

// Now, if templates were covariants, a vector<Apple*> could be
// cast to a vector<Fruit*>
vector<Fruit*> & vf = va;
vf.push_back(new Orange); // Bam, we just added an Orange among the Apples!

要实现您要执行的操作,SmartPointer类必须具有模板化构造函数,该构造函数可以使用另一个SmartPointer或另一个类型的指针。你可以看一下boost :: shared_ptr,它就是这样做的。

template <typename T>
class SmartPointer {

    T * ptr;

  public:
    SmartPointer(T * p) : ptr(p) {}
    SmartPointer(const SmartPointer & sp) : ptr(sp.ptr) {}

    template <typename U>
    SmartPointer(U * p) : ptr(p) {}

    template <typename U>
    SmartPointer(const SmartPointer<U> & sp) : ptr(sp.ptr) {}

    // Do the same for operator= (even though it's not used in your example)
};

答案 3 :(得分:3)

取决于SmartPtr课程。如果它有一个复制构造函数(或者在你的情况下,赋值运算符),它采用SmartPtr<T>,其中T是它构造的类型,那么它不会起作用,因为SmartPtr<T1>是无关的即使T1和T2通过继承相关,也要SmartPtr<T2>

但是,如果SmartPtr有一个模板化的复制构造函数/赋值运算符,模板参数TOther,它接受​​SmartPtr<TOther>,那么它应该可以工作。

答案 4 :(得分:2)

假设您已控制SmartPtr类,解决方案是提供模板化构造函数:

template <class T>
class SmartPtr
{
    T *ptr;
public:

    // Note that this IS NOT a copy constructor, just another constructor that takes 
    // a similar looking class.
    template <class O>
    SmartPtr(const SmartPtr<O> &src)
    {
        ptr = src.GetPtr();
    }
    // And likewise with assignment operator.
};

如果T和O类型兼容,它将起作用,如果它们不是你将得到编译错误。

答案 5 :(得分:0)

我认为最简单的方法是根据以下内容自动转换为另一个SmartPtr:

template <class T>
class SmartPtr
{
public:
    SmartPtr(T *ptr) { t = ptr; }
    operator T * () const { return t; }
    template <class Q> operator SmartPtr<Q> () const
    { return SmartPtr<Q>(static_cast<Q *>(static_cast<T *>(* this))); }
private:
    T *t;
};

请注意,在转换运算符模板不需要知道智能指针的语义的意义上,此实现是健壮的,因此引用计数不需要复制等。

相关问题