簡體   English   中英

C ++協變模板

[英]C++ covariant templates

我覺得之前已經問過這個問題,但我無法在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包裝在另一個對象中時...任何快捷方式?

SmartPtr<Base>SmartPtr<Derived>SmartPtr模板的兩個不同實例。 這些新類不共享BaseDerived所做的繼承。 因此,你的問題。

執行此賦值操作的最佳方法是什么?

 SmartPtr<Base> b = d; 

不調用賦值運算符。 這會調用copy-ctor(大多數情況下都會刪除副本),就像你寫的那樣:

 SmartPtr<Base> b(d); 

提供一個帶有SmartPtr<OtherType>並實現它的copy-ctor。 賦值運算符也是如此。 你必須寫出copy-ctor和op =記住SmartPtr的語義。

復制構造函數和賦值運算符都應該能夠采用不同類型的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
};

模板不是協變的,這很好; 想象在下列情況下會發生什么:

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)
};

取決於SmartPtr類。 如果它有一個復制構造函數(或者在你的情況下,賦值運算符),它采用SmartPtr<T> ,其中T是它的構造類型,那么它就無法工作,因為SmartPtr<T1>SmartPtr<T2>無關SmartPtr<T2>即使T1和T2通過繼承相關。

但是,如果SmartPtr具有模板化的復制構造函數/賦值運算符,並且模板參數TOther接受SmartPtr<TOther> ,那么它應該可以工作。

假設您已控制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類型兼容,它將工作,如果它們不是你將得到編譯錯誤。

我認為最簡單的方法是根據以下內容自動轉換為另一個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;
};

請注意,在轉換運算符模板不需要知道智能指針的語義的意義上,此實現是健壯的,因此引用計數不需要復制等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM