Aquí podría ser tu PUBLICIDAD


Plantillas de covariantes C ++

votos
16

Siento que se me ha preguntado antes, pero no puedo encontrarlo en SO, ni puedo encontrar nada útil en Google. Tal vez covariante no es la palabra que estoy buscando, pero este concepto es muy similar a los tipos de retorno covariante en las funciones, por lo que creo que es probablemente correcto. Esto es lo que quiero hacer y me da un error de compilación:

class Base;
class Derived : public Base;

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

Supongamos que esas clases están completamente desarrolladas ... Creo que entiendes la idea. No puede convertir SmartPtr<Derived>a a SmartPtr<Base>por alguna razón no clara. Recuerdo que esto es normal en C ++ y en muchos otros idiomas, aunque por el momento no puedo recordar por qué.

Mi pregunta de raíz es: ¿cuál es la mejor manera de realizar esta operación de asignación? Actualmente, estoy sacando el puntero de SmartPtr, actualizándolo explícitamente al tipo base, y luego envolviéndolo en uno nuevo SmartPtrdel tipo apropiado (nótese que esto no está filtrando recursos porque nuestra SmartPtrclase local utiliza el recuento de referencias intrusivas). Eso es largo y desordenado, especialmente cuando necesito envolver SmartPtrotro objeto ... ¿cualquier atajo?

Publicado el 12/03/2009 a las 16:46
fuente por usuario rmeador
En otros idiomas...        العربية       

6 respuestas

votos
15

SmartPtr<Base>y SmartPtr<Derived>son dos instancias distintas de una SmartPtrplantilla. Estas nuevas clases no comparten la herencia que Basey Derivedhacen. Por lo tanto, tu problema.

¿Cuál es la mejor manera de realizar esta operación de asignación?

 SmartPtr<Base> b = d; 

No invoca al operador de asignación. Esto invoca el copy-ctor (la copia se elimina en la mayoría de los casos) y es exactamente como si hubiera escrito:

 SmartPtr<Base> b(d); 

Proporcione un copiador que tome SmartPtr<OtherType>e impleméntelo. Lo mismo aplica para el operador de asignación. Tendrá que escribir el copy-ctor y op = teniendo en cuenta la semántica de SmartPtr.

Respondida el 12/03/2009 a las 04:57
fuente por usuario dirkgently


Aquí podría ser tu PUBLICIDAD


votos
11

Tanto el constructor de copia como el operador de asignación deberían poder tomar un SmartPtr de un tipo diferente e intentar copiar el puntero de uno a otro. Si los tipos no son compatibles, el compilador se quejará, y si son compatibles, habrá resuelto su problema. Algo como esto:

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
};
Respondida el 12/03/2009 a las 04:56
fuente por usuario MSN

votos
5

Las plantillas no son covariantes, y eso es bueno; imagina lo que sucedería en el siguiente caso:

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!

Para lograr lo que está tratando de hacer, la clase SmartPointer debe tener un constructor con plantilla, que tome otro SmartPointer o un puntero de otro tipo. Puedes echar un vistazo a boost :: shared_ptr, que hace exactamente eso.

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)
};
Respondida el 12/03/2009 a las 05:02
fuente por usuario Luc Touraille

votos
3

Depende de la SmartPtrclase. Si tiene un constructor de copia (o en su caso, operador de asignación) que toma SmartPtr<T>, donde T es el tipo con el que se construyó, entonces no va a funcionar, porque SmartPtr<T1>no está relacionado SmartPtr<T2>aunque T1 y T2 estén relacionados por herencia. .

Sin embargo, si tiene una SmartPtr a plantillas operador constructor de copia / asignación, con el parámetro de plantilla TOther, que acepta SmartPtr<TOther>, entonces debería funcionar.

Respondida el 12/03/2009 a las 04:56
fuente por usuario Daniel Earwicker

votos
2

Suponiendo que tiene el control de la clase SmartPtr, la solución es proporcionar un constructor con plantilla:

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

Si los tipos T y O son compatibles, funcionará; si no lo hacen, obtendrás un error de compilación.

Respondida el 12/03/2009 a las 04:56
fuente por usuario Eclipse

votos
0

Creo que lo más fácil es proporcionar la conversión automática a otro SmartPtr de acuerdo con lo siguiente:

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

Tenga en cuenta que esta implementación es robusta en el sentido de que la plantilla de operador de conversión no necesita conocer la semántica del puntero inteligente, por lo que el recuento de referencias no necesita ser replicado, etc.

Respondida el 12/03/2009 a las 06:26
fuente por usuario Antti Huima