C ++ covariantă șabloane

voturi
16

Mă simt ca aceasta a fost întrebat înainte, dar nu pot să-l găsiți pe SO, și nici nu pot găsi ceva util pe Google. Poate „covariantă“ nu este cuvântul caut, dar acest concept este foarte similar cu covariant tipuri de returnare pe funcții, deci cred că e probabil corect. Iată ce vreau să fac și dă-mi o eroare compilator:

class Base;
class Derived : public Base;

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

Să presupunem că aceste clase sunt pe deplin prescurtate afară ... Cred că ai prins ideea. Nu se poate converti o SmartPtr<Derived>într - o dintr- SmartPtr<Base>un motiv neclar. Îmi aduc aminte că acest lucru este normal în C ++ și multe alte limbi, deși în acest moment nu pot să amintesc de ce.

Întrebarea mea rădăcină este: ceea ce este cel mai bun mod de a efectua această operație misiune? În prezent, sunt trăgând indicatorul afară din SmartPtr, acesta upcasting în mod explicit la tipul de bază, apoi într - un ambalaj nou SmartPtrde tip corespunzător (rețineți că acest lucru nu se scurge resurse , deoarece ne-cultivate acasă SmartPtrclasa folosește de numărare de referință intruziv). Asta e lung și murdar, mai ales atunci când atunci am nevoie să împachetați SmartPtrîntr - un alt obiect ... comenzi rapide?

Întrebat 12/03/2009 la 14:46
sursa de către utilizator
În alte limbi...                            


6 răspunsuri

voturi
3

Depinde de SmartPtrclasa. În cazul în care are un constructor de copiere (sau în cazul dumneavoastră, operatorul de atribuire) care ia SmartPtr<T>, în cazul în care T este tipul care a fost construit cu, atunci nu va funcționa, pentru că nu SmartPtr<T1>are nicio legătură , SmartPtr<T2>chiar dacă T1 și T2 sunt legate prin moștenire .

Cu toate acestea, în cazul în care SmartPtr are un templatized operator de constructorul de copiere / atribuire, cu parametrul șablon TOther, care acceptă SmartPtr<TOther>, atunci ar trebui să funcționeze.

Publicat 12/03/2009 la 14:56
sursa de către utilizator

voturi
11

Atât constructorul de copiere și operatorul de atribuire ar trebui să fie în măsură să ia o SmartPtr de un tip diferit și încercarea de a copia indicatorul de la unul la altul. În cazul în care tipurile nu sunt compatibile, compilatorul va plânge, iar dacă acestea sunt compatibile, v-ați rezolvat problema. Ceva de genul:

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
};
Publicat 12/03/2009 la 14:56
sursa de către utilizator

voturi
2

Presupunând că aveți un control al clasei SmartPtr, soluția este de a oferi un constructor templated:

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

În cazul în care tipurile T și o sunt compatibile, acesta va funcționa, în cazul în care nu veți obține o eroare de compilare.

Publicat 12/03/2009 la 14:56
sursa de către utilizator

voturi
15

SmartPtr<Base>și SmartPtr<Derived>sunt două instanțieri distincte ale pe SmartPtrșablon. Aceste noi clase nu împărtășesc moștenirea pe care Baseși Derivedfac. Prin urmare, problema ta.

ceea ce este cel mai bun mod de a efectua această operație misiune?

 SmartPtr<Base> b = d; 

nu invocă operatorul de atribuire. Aceasta invocă copy-ctor (copia este elided în cele mai multe cazuri) și este exact ca si cum ai scris:

 SmartPtr<Base> b(d); 

Furnizarea unei copii-ctor care ia o SmartPtr<OtherType>și punerea în aplicare a acesteia. Același lucru este valabil pentru operatorul de atribuire. Va trebui să scrie copy-ctor si op = tinand cont de semantica SmartPtr.

Publicat 12/03/2009 la 14:57
sursa de către utilizator

voturi
5

Șabloanele nu sunt covariant, și asta e bine; imaginați-vă ce s-ar întâmpla în cazul următor:

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!

Pentru a realiza ceea ce încercați să faceți, clasa SmartPointer trebuie să aibă un constructor templatized, care ia fie un alt SmartPointer sau un pointer de alt tip. Ai putea avea o privire la impuls :: shared_ptr, care face exact acest lucru.

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)
};
Publicat 12/03/2009 la 15:02
sursa de către utilizator

voturi
0

Cred că cel mai ușor lucru este de a oferi conversia automată la un alt SmartPtr în conformitate cu următoarele:

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

Rețineți că această implementare este robust, în sensul că șablonul de conversie operatorul nu trebuie să știe despre semantica indicatorul inteligent, astfel încât de numărare de referință nu trebuie să reproduse, etc.

Publicat 12/03/2009 la 16:26
sursa de către utilizator

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more