Chocó con otro plantillas problema:

El problema: quiero parcialmente se especializan en un contenedor de clase (foo) para el caso de que los objetos son punteros, y quiero especializarme sólo la elimina-método. Debería tener este aspecto:

El código lib

template <typename T>
class foo
{
public:
    void addSome    (T o) { printf ("adding that object..."); }
    void deleteSome (T o) { printf ("deleting that object..."); }
};

template <typename T>
class foo <T *>
{
public:
    void deleteSome (T* o) { printf ("deleting that PTR to an object..."); }
};

El código de usuario

foo<myclass> myclasses;
foo<myclass*> myptrs;

myptrs.addSome (new myclass());

Esto se traduce en que el compilador me dice que myptrs no tiene un método llamado addSome.
Por qué ?

Gracias.


Solución

basado en tony responde aquí totalmente compilable cosas


lib

template <typename T>
class foobase
{
public:
    void addSome    (T o) { printf ("adding that object..."); }
    void deleteSome (T o) { printf ("deleting that object..."); }
};


template <typename T>
class foo : public foobase<T>
{ };

template <typename T>
class foo<T *> : public foobase<T *>
{
public:
    void deleteSome (T* o) { printf ("deleting that ptr to an object..."); }
};

usuario

foo<int>    fi;
foo<int*>   fpi;

int         i = 13;

fi.addSome (12);            
fpi.addSome (&i);

fpi.deleteSome (12);        //compiler-error: doesnt work
fi.deleteSome (&i);         //compiler-error: doesnt work
fi.deleteSome (12);         //foobase::deleteSome called
fpi.deleteSome (&i);        //foo<T*>::deleteSome called
Solución equivocada. Supongamos que utilizamos foo<int*>. Supongamos que algunas de las funciones del class foobase llamadas deleteSome. ¿Qué función se llamará? A la derecha! deleteSome(T) de la class foobase en lugar de deleteSome(T *).
Pregunta Similar: stackoverflow.com/questions/1501357/…
Creo que el punto es que foobase no debe ser utilizado. Es allí sólo para hacer el hack posible.
pero será se utiliza de forma implícita si se llama a una función que no sea deleteSome. En ese punto, si la función llamada llamadas deleteSome, nunca llegar a la función en la que se derivan classs.
¿Qué pasa si la clase base especifica la función a ser virtual (y lo que no instantiatede)? He visto este problema / patrón de diseño a la fluencia antes: ¿tiene un nombre?

OriginalEl autor Roman Pfneudl | 2009-11-18

5 Comentarios

  1. 11

    Segunda solución (correcta)

    template <typename T>
    class foo
    {
    public:
        void addSome    (T o) { printf ("adding that object..."); } 
        void deleteSome(T o) { deleteSomeHelper<T>()(o); }
    protected:
        template<typename TX> 
        struct deleteSomeHelper { void operator()(TX& o) { printf ("deleting that object..."); } };
        template<typename TX> 
        struct deleteSomeHelper<TX*> { void operator()(TX*& o) { printf ("deleting that PTR to an object..."); } };
    };

    Esta solución es válida de acuerdo a Centro De La Cuestión #727.


    Primera (incorrecto) solución: (se mantiene esta como comentarios se refieren a ella)

    Usted puede especializarse sólo una parte de la clase. En tu caso, la mejor manera es función de sobrecarga deleteSome de la siguiente manera:

    template <typename T>
    class foo
    {
    public:
        void addSome    (T o) { printf ("adding that object..."); }
        void deleteSome (T o) { printf ("deleting that object..."); }
        void deleteSome (T* o) { printf ("deleting that object..."); }
    };
    Bueno, eso es una buena sugerencia, pero también permite el siguiente caso para compilar sin errores: – foo<int> fi; int i; fi.deleteSome (&i); Donde se debe, por supuesto, sólo ser capaz de «deleteSome» de tipo int
    Que no va a funcionar. Supongamos que el uso de foo<miclase*>. A continuación, foo tiene 3 funciones: addSome (myclass * o), deleteSome (myclass * o) y deleteSome (myclass ** o). Última función nunca será llamado.
    Añadido segunda solución.
    voy a ir de compras ahora. Pero voy a dejar una nota: Usted no puede poner el explícito especialización para el miembro de la plantilla en el cuerpo de la clase. Usted también puede poner fuera de ella, como esto no va a funcionar: template<typename T> template<> void foo<T>::deleteSomeHelper<false>(T &o) { } (como no puede ser de cualquier parámetro de plantilla de las cláusulas antes de un template<>).
    Ahora puedo ver lo que está hablando. Fijo.

    OriginalEl autor Kirill V. Lyadvinsky

  2. 10

    Otra solución. Utilizar el auxiliar de la función deleteSomeHelp.

    template <typename T>
    class foo {
     public:    
       void addSome    (T o) { printf ("adding that object..."); 
       template<class R>
       void deleteSomeHelp (R   o) { printf ("deleting that object..."); }};
       template<class R>
       void deleteSomeHelp (R * o) { printf ("deleting that PTR to an object..."); }};
       void deleteSome (T o) { deleteSomeHelp(o); }
    }    
    cosas locas :), pero estoy enfrentando el mismo problema aquí, así que un foo<int> PUEDE llamar a un deleteSome con un puntero dado.
    +1 se Parece a la que yo iba a usar de inmediato. Es mucho más simple y hace el trabajo.

    OriginalEl autor Alexey Malistov

  3. 3

    No he visto esta solución sin embargo, el uso de boost enable_if, is_same y remove_pointer para obtener dos funciones en una clase, sin ningún tipo de herencia u otro tipo de información antigua.

    Ver más abajo para una versión que sólo remove_pointer.

    #include <boost\utility\enable_if.hpp>
    #include <boost\type_traits\is_same.hpp>
    #include <boost\type_traits\remove_pointer.hpp>
    
    template <typename T>
    class foo
    {
    public:
        typedef typename boost::remove_pointer<T>::type T_noptr;
    
        void addSome    (T o) { printf ("adding that object..."); }
    
        template<typename U>
        void deleteSome (U o, typename boost::enable_if<boost::is_same<T_noptr, U>>::type* dummy = 0) { 
            printf ("deleting that object..."); 
        }
        template<typename U>
        void deleteSome (U* o, typename boost::enable_if<boost::is_same<T_noptr, U>>::type* dummy = 0) { 
            printf ("deleting that PTR to that object..."); 
        }
    };

    Una versión simplificada es:

    #include <cstdio>
    #include <boost\type_traits\remove_pointer.hpp>
    
    template <typename T>
    class foo
    {
    public:
        typedef typename boost::remove_pointer<T>::type T_value;
    
        void addSome    (T o) { printf ("adding that object..."); }
    
        void deleteSome (T_value& o) { //need ref to avoid auto-conv of double->int
            printf ("deleting that object..."); 
        }
    
        void deleteSome (T_value* o) { 
            printf ("deleting that PTR to that object..."); 
        }
    };

    Y funciona en MSVC 9: (comentado líneas que dan errores, como son incorrectos, pero es bueno tener para las pruebas)

    void main()
    {
       foo<int> x;
       foo<int*> y;
    
       int a;
       float b;
    
       x.deleteSome(a);
       x.deleteSome(&a);
       //x.deleteSome(b); //doesn't compile, as it shouldn't
       //x.deleteSome(&b);
       y.deleteSome(a);
       y.deleteSome(&a);
       //y.deleteSome(b);
       //y.deleteSome(&b);
    }
    Que es muy interesante

    OriginalEl autor Macke

  4. 2

    Crear la clase base para una sola función deleteSome

    template<class T>
    class base {
    public:
      void deleteSome (T o) { printf ("deleting that object..."); }
    }

    Parcial de especialización

    template<class T>
    class base<T*> {
    public:
      void deleteSome (T * o) { printf ("deleting that PTR to an object..."); }
    }

    El uso de su base de clase

    template <typename T>
    class foo : public base<T> {
     public:    
       void addSome    (T o) { printf ("adding that object..."); 
    }    

    OriginalEl autor Alexey Malistov

  5. 1

    Puede utilizar la herencia para conseguir que esto funcione :

    template <typename T>
    class foobase
    {
    public:
        void addSome    (T o) { printf ("adding that object..."); }
        void deleteSome (T o) { printf ("deleting that object..."); }
    };
    
    template <typename T>
    class foo : public foobase<T>
    { };
    
    template <typename T>
    class foo <T *> : public foobase<T>
    {
    public:
        void deleteSome (T* o) { printf ("deleting that PTR to an object..."); }
    };
    Gracias tony, supongo que im que va con su solución.
    Esta solución no es escalable. Supongamos que deseas añadir una nueva función de haceralgo(T o) y se especializan para el caso de que los objetos son std::list<T>.
    También esta solución es incorrecta. Suponemos que el uso de foo<int*>. Ninguna función de la class foobase puede llamar deleteSome porque deleteSome (T) será llamado en lugar de deleteSome (T*).
    Um, no debería ser class foo<T*> : public foobase<T*> (nota: la segunda *)?
    Kirill la respuesta se pierde uno de los usos para la especialización, es decir, se especializan una clase existente para su propio nuevo tipo (sin tener la capacidad de modificar los existentes de clase). Tony solución es la más general.

    OriginalEl autor

Dejar respuesta

Please enter your comment!
Please enter your name here