He oído que clase de C++ de la función miembro plantillas no puede ser virtual. ¿Es esto cierto?

Si pueden ser virtuales, lo que es un ejemplo de un escenario en el que se podría utilizar una función de este tipo?

Me enfrenté a un problema similar, y también aprendí que es motivo de controversia para ser virtual y la plantilla al mismo tiempo. Mi solución fue la de escribir la plantilla de magia que va a ser común entre las clases derivadas y llamada a una función virtual pura que hace la parte especializada. Este es, por supuesto, relacionadas con la naturaleza de mi problema, así que puede que no funcione en todos los casos.

OriginalEl autor WannaBeGeek | 2010-03-01

11 Comentarios

  1. 279

    Plantillas son todos acerca de que el compilador genere código en en tiempo de compilación. Las funciones virtuales son todo sobre el sistema de tiempo de ejecución que se tiene que determinar la función de llamada en tiempo de ejecución.

    Una vez que el sistema de tiempo de ejecución imaginé que sería necesario llamar a un templatized función virtual, la compilación se hace y el compilador no puede generar la instancia adecuada ya. Por lo tanto, usted no puede tener la función miembro virtual de plantillas.

    Sin embargo, hay unos pocos poderosos, y diversas técnicas derivadas de la combinación de polimorfismo de las plantillas y, en particular, los llamados tipo de borrado.

    Yo no estoy viendo un idioma razón de esto, sólo aplicación razones. vtables no son parte del lenguaje, que sólo la forma estándar de los compiladores de implementar el idioma.
    Virtual functions are all about the run-time system figuring out which function to call at run-time – lo siento, pero esta es una muy mala manera, y bastante confuso. Es sólo direccionamiento indirecto, y que no hay «tiempo de ejecución de averiguar», es conocido en tiempo de compilación que la función es el señalado por el n-ésimo puntero en la vtable. «Averiguar» implica que hay comprobaciones de tipo y tal, que no es el caso. Once the run-time system figured out it would need to call a templatized virtual function – si o no la función es virtual, es conocido en tiempo de compilación.
    1. Si los compiladores ve void f(concr_base& cb, virt_base& vb) { cb.f(); vb.f(); }, entonces se «sabe» que se invoca la función en el punto cb.f() llamado, y no sabe que para vb.f(). El último tiene que ser descubierto en tiempo de ejecución, por el sistema de tiempo de ejecución. Si desea llamar a este «averiguar», y si esto es más o menos eficiente, no cambia estos hechos un poco.
    2. ejemplos de (miembro) de la función de plantillas (miembro) de las funciones, así que no hay problema con poner un puntero a dicha instancia en la vtable. Pero que la plantilla de instancias son necesarias sólo se conoce cuando el que llama es compilado, mientras que las vtables se configura cuando la clase base y clases derivadas son compilados. Y estos son compilados por separado. Peor aún – de nuevo las clases derivadas pueden estar vinculados a los sistemas en ejecución en tiempo de ejecución (piensa en tu navegador cargar un plugin de forma dinámica). Incluso el código fuente del autor de la llamada podría ser perdido cuando una nueva clase derivada es creado.
    ¿Por qué estás haciendo suposiciones basadas en mi nombre? Yo no confundir los medicamentos genéricos y de plantillas. Sé que Java genéricos son puramente tiempo de ejecución. Usted no exhaustiva de explicar por qué usted no puede tener la función miembro virtual de plantillas en C++, pero InQsitive hizo. Excesivamente simplificada de la plantilla y virtual de mecánica de ‘tiempo de compilación’ vs ‘tiempo de ejecución» y concluyó que «no se puede tener función miembro virtual de plantillas.» Yo hice referencia a InQsitive la respuesta, que hace referencia a «las Plantillas de C++ La Guía Completa». No considero que sea «mano saludando». Que tengan un buen día.

    OriginalEl autor sbi

  2. 95

    De Las Plantillas De C++ La Guía Completa:

    Función de miembro de plantillas no puede ser declarado como virtual. Esta restricción
    se impone, porque la aplicación usual de la función virtual
    llame utiliza el mecanismo de un tamaño fijo de la tabla con una entrada por la virtual
    la función. Sin embargo, el número de instancias de un miembro de la función
    la plantilla no es fijo hasta que todo el programa ha sido traducido.
    Por lo tanto, apoyar la función miembro virtual de plantillas requeriría
    soporte para un nuevo tipo de mecanismo en los compiladores de C++ y
    enlazadores. En contraste, los miembros ordinarios de plantillas de clase puede ser
    virtual, dado que su número es fijo cuando una clase es instanciada

    OriginalEl autor InQusitive

  3. 30

    C++ no permite virtual funciones de miembro de plantilla ahora mismo. La razón más probable es la complejidad de la implementación. Rajendra da una buena razón por la que no se puede hacer ahora, pero podría ser posible con cambios razonables de la norma. Especialmente trabajando cuántas instancias de una función de plantilla existen en la realidad y en la construcción de la vtable parece difícil si se considera el lugar de la llamada de función virtual. Los estándares de las personas sólo tienen un montón de otras cosas que hacer ahora y C++1x es un montón de trabajo para el compilador de escritores.

    Cuando usted necesita una plantilla de función miembro? Una vez me encontré con una situación en la que traté de rehacer una jerarquía con un puro virtual de la clase base. Era un estilo pobre para la implementación de diferentes estrategias. Yo quería cambiar el argumento de una de las funciones virtuales a un tipo numérico y en lugar de sobrecargar la función de miembro y reemplazar cada una sobrecarga en todas las sub-clases traté de usar virtual de la plantilla de funciones (y tenía que averiguar de que no existen.)

    Una función virtual podría ser llamado a partir de un código que ni siquiera existían cuando la función fue compilado. Cómo sería el compilador de determinar qué instancias de un (teórico) virtual miembro de plantilla de función para generar código que aún no existe?
    Sí, compilación independiente sería un gran problema. No soy un experto en los compiladores de C++ por lo que no puedo ofrecer una solución. Como con funciones con plantillas en general, se deben crear instancias de nuevo en cada unidad de compilación, derecho? No se que resolver el problema?
    si te refieres a la carga dinámica de librerías, que es un problema general con la plantilla de las clases / funciones, no sólo con virtual métodos de plantilla.
    «C++ no permite […]» – agradecería para ver la referencia de la norma (no importa si el que hasta la fecha cuando la respuesta fue escrito o la que hasta la fecha ocho años más tarde)…

    OriginalEl autor pmr

  4. 16

    Función Virtual Tablas

    Vamos a empezar con algunos antecedentes sobre la función virtual tablas y cómo funcionan (fuente):

    [20.3] ¿Cuál es la diferencia entre lo virtual y no-virtual
    las funciones miembro se llama?

    No virtual las funciones miembro se resuelve de forma estática. Es decir, la
    la función miembro es seleccionado de forma estática (en tiempo de compilación), basado en la
    tipo del puntero (o de referencia) para el objeto.

    En contraste, miembro virtual de funciones se resuelven de forma dinámica (en
    en tiempo de ejecución). Es decir, la función miembro es seleccionado de forma dinámica (en
    en tiempo de ejecución), basado en el tipo del objeto, no el tipo de la
    puntero o referencia a ese objeto. Esto se llama «dinámica de unión.»
    La mayoría de los compiladores utilizan alguna variante de la técnica siguiente: si el
    objeto tiene una o más funciones virtuales, el compilador pone un oculto
    puntero en el objeto llamado «virtual-puntero» o «v-puntero.» Este
    v-puntos de puntero a una tabla global llamado «virtual-tabla» o
    «de la tabla v.»

    El compilador crea un v-tabla para cada clase que tiene al menos una
    la función virtual. Por ejemplo, si la clase Círculo tiene funciones virtuales
    para dibujar() y mover() y cambiar el tamaño de(), no sería exactamente una tabla v
    asocia con la clase Circle, incluso si hay un trillón de Círculo
    los objetos, y la v-puntero de cada uno de esos Círculo los objetos que apuntar
    para el Círculo de la tabla v. La tabla v sí tiene punteros a cada uno de los
    las funciones virtuales en la clase. Por ejemplo, el Círculo v-tabla de
    tiene tres punteros: un puntero a un Círculo::draw(), un puntero a
    Círculo::move(), y un puntero a un Círculo::cambio de tamaño().

    Durante una expedición de una función virtual, el sistema de tiempo de ejecución de la siguiente manera
    el objeto de la v-puntero a la clase de la tabla v, luego sigue la
    ranura correspondiente en la tabla v del código del método.

    El espacio-costes de la técnica anterior es nominal: un extra
    puntero al objeto (pero sólo para los objetos que se necesitan para hacer dinámico
    el enlace), además de un extra puntero por método, pero sólo para virtual
    los métodos). El tiempo-coste de sobrecarga es también bastante nominal: en comparación con un
    la función normal de la llamada, una llamada de función virtual requiere de dos extra
    recopila (uno para obtener el valor de la v-puntero, un segundo para conseguir la
    discurso del método). Ninguno de este tiempo de ejecución de la actividad que ocurre con
    no virtual funciones, ya que el compilador resuelve la no-virtual
    funciones exclusivamente en tiempo de compilación basado en el tipo de la
    puntero.


    Mi problema, o cómo llegué aquí

    Estoy tratando de usar algo como esto ahora para una cubefile de la clase base con plantilla de carga optimizado funciones que se implementa de forma diferente para diferentes tipos de cubos (algunos almacenados por píxel, algunos por imagen, etc).

    Código:

    virtual void  LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
    virtual void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
    virtual void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

    Lo que me gustaría ser, pero no de compilación debido a una virtual con plantilla combo:

    template<class T>
        virtual void  LoadCube(UtpBipCube<T> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
                long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

    me terminó de mover la declaración de la plantilla al nivel de la clase. Esta solución habría forzado a los programas para saber acerca de los tipos específicos de datos que iba a leer antes de leer, lo cual es inaceptable.

    Solución

    advertencia, esto no es muy bonita, pero me permitió eliminar repetitivo código de ejecución de

    1) en la clase base

    virtual void  LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
    virtual void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
    virtual void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

    2) y en los niños de las clases

    void  LoadCube(UtpBipCube<float> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
    { LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
    
    void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
    { LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
    
    void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
    { LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }
    
    template<class T>
    void  LoadAnyCube(UtpBipCube<T> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1);

    Nota que LoadAnyCube no está declarado en la clase base.


    He aquí otro desbordamiento de la pila de responder con un trabajo en torno a:
    necesita un virtual miembro de plantilla de solución.

    OriginalEl autor Mark Essel

  5. 12

    El siguiente código puede ser compilado y se ejecuta correctamente, el uso de MinGW G++ 3.4.5 en la Ventana de 7:

    #include <iostream>
    #include <string>
    
    using namespace std;
    
    template <typename T>
    class A{
    public:
        virtual void func1(const T& p)
        {
            cout<<"A:"<<p<<endl;
        }
    };
    
    template <typename T>
    class B
    : public A<T>
    {
    public:
        virtual void func1(const T& p)
        {
            cout<<"A<--B:"<<p<<endl;
        }
    };
    
    int main(int argc, char** argv)
    {
        A<string> a;
        B<int> b;
        B<string> c;
    
        A<string>* p = &a;
        p->func1("A<string> a");
        p = dynamic_cast<A<string>*>(&c);
        p->func1("B<string> c");
        B<int>* q = &b;
        q->func1(3);
    }

    y el resultado es:

    A:A<string> a
    A<--B:B<string> c
    A<--B:3

    Y después he añadido una nueva clase X:

    class X
    {
    public:
        template <typename T>
        virtual void func2(const T& p)
        {
            cout<<"C:"<<p<<endl;
        }
    };

    Cuando traté de usar la clase X en main() como este:

    X x;
    x.func2<string>("X x");

    g++ informe de error siguientes:

    vtempl.cpp:34: error: invalid use of `virtual' in template declaration of `virtu
    al void X::func2(const T&)'

    Así que es obvio que:

    • función miembro virtual puede ser utilizado en una plantilla de clase. Es fácil para el compilador para la construcción de vtable
    • Es imposible definir una clase miembro de plantilla de función como virtual, como se puede ver, es difícil determinar la función de la firma y asignar vtable entradas.
    Una plantilla de clase pueden tener funciones miembro virtuales. Una función miembro no puede ser a la vez un miembro de la plantilla de función y una función miembro virtual.
    es realmente un error con gcc 4.4.3. En mi sistema para asegurarse de Ubuntu 10.04
    Esto es totalmente diferente de lo que la pregunta formulada. Aquí toda la clase base es de plantilla. He compilado este tipo de cosas antes. Este sería compilar en Visual Studio 2010 también

    OriginalEl autor Brent81

  6. 9

    No, No pueden. Pero:

    template<typename T>
    class Foo {
    public:
      template<typename P>
      void f(const P& p) {
        ((T*)this)->f<P>(p);
      }
    };
    
    class Bar : public Foo<Bar> {
    public:
      template<typename P>
      void f(const P& p) {
        std::cout << p << std::endl;
      }
    };
    
    int main() {
      Bar bar;
    
      Bar *pbar = &bar;
      pbar -> f(1);
    
      Foo<Bar> *pfoo = &bar;
      pfoo -> f(1);
    };

    tiene el mismo efecto si todo lo que quiero hacer es tener una interfaz común y aplazar la aplicación a las subclases.

    OriginalEl autor Tom

  7. 3

    Para responder a la segunda parte de la pregunta:

    Si pueden ser virtuales, lo que es un ejemplo de un escenario en el que se podría utilizar una función de este tipo?

    Esto no es irrazonable cosa que queramos hacer. Por ejemplo, Java (donde cada método es virtual) no tiene problemas con los métodos genéricos.

    Un ejemplo en C++ de querer una función virtual de la plantilla es un miembro de la función que acepta un genérico de iterador. O un miembro de la función que acepta una función genérica objeto.

    La solución a este problema es utilizar el tipo de borrado con boost::any_range y boost::función, que le permite aceptar un genérico iterador o functor sin necesidad de realizar su función de una plantilla.

    Java genéricos son azúcar sintáctico para la fundición. Ellos no son el mismo como plantillas.
    Se podría decir que la conversión es la forma en Java implementa genéricos, en lugar de al revés… y sematically, el caso de uso exclipy presenta es válida la OMI.

    OriginalEl autor exclipy

  8. 3

    No, funciones de miembro de plantilla no puede ser virtual.

    Mi curiosidad es: ¿por Qué? ¿Qué problemas el compilador caras en hacerlo?
    Usted necesita una declaración de alcance (al menos, con el fin de obtener el tipo correcto). Es requerido por la norma (y el lenguaje) para tener una declaración en el ámbito de aplicación de los identificadores de utilizar.

    OriginalEl autor dirkgently

  9. 2

    Hay una solución para ‘virtual método de plantilla’ si el conjunto de tipos para el método de plantilla se conoce de antemano.

    Para mostrar la idea, en el siguiente ejemplo sólo dos tipos son utilizados (int y double).

    Allí, un «virtual» de la plantilla de método (Base::Method) llamadas correspondientes método virtual (uno de Base::VMethod) que, a su vez, llama a la plantilla de la implementación del método (Impl::TMethod).

    Solo se debe implementar el método de plantilla TMethod en derivados de implementaciones (AImpl, BImpl) y el uso Derived<*Impl>.

    class Base
    {
    public:
    virtual ~Base()
    {
    }
    template <typename T>
    T Method(T t)
    {
    return VMethod(t);
    }
    private:
    virtual int VMethod(int t) = 0;
    virtual double VMethod(double t) = 0;
    };
    template <class Impl>
    class Derived : public Impl
    {
    public:
    template <class... TArgs>
    Derived(TArgs&&... args)
    : Impl(std::forward<TArgs>(args)...)
    {
    }
    private:
    int VMethod(int t) final
    {
    return Impl::TMethod(t);
    }
    double VMethod(double t) final
    {
    return Impl::TMethod(t);
    }
    };
    class AImpl : public Base
    {
    protected:
    AImpl(int p)
    : i(p)
    {
    }
    template <typename T>
    T TMethod(T t)
    {
    return t - i;
    }
    private:
    int i;
    };
    using A = Derived<AImpl>;
    class BImpl : public Base
    {
    protected:
    BImpl(int p)
    : i(p)
    {
    }
    template <typename T>
    T TMethod(T t)
    {
    return t + i;
    }
    private:
    int i;
    };
    using B = Derived<BImpl>;
    int main(int argc, const char* argv[])
    {
    A a(1);
    B b(1);
    Base* base = nullptr;
    base = &a;
    std::cout << base->Method(1) << std::endl;
    std::cout << base->Method(2.0) << std::endl;
    base = &b;
    std::cout << base->Method(1) << std::endl;
    std::cout << base->Method(2.0) << std::endl;
    }

    De salida:

    0
    1
    2
    3

    NB:
    Base::Method es en realidad la plusvalía para el código real (VMethod puede ser hecho público y se usa directamente).
    Lo he añadido para que se vea como un verdadero «virtual» de la plantilla de método.

    Se me ocurrió esta solución mientras que la solución de un problema en el trabajo. Parece similar a la Marca de Essel la de arriba, pero espero que sea mejor implementado y explicado.
    Yo ya había calificar esto como ofuscación de código, y aún así no consigue evitar el hecho de que haya que modificar el original Base clase cada vez que usted necesita para llamar a una función de la plantilla con un argumento de tipo no compatible a las realizadas hasta ahora. Evitando de esta necesidad es la intención de plantillas…
    Essels enfoque es totalmente diferente: Ordinaria funciones virtuales aceptar diferentes de la plantilla de los casos – y el final de la plantilla de función en la clase derivada sólo sirve para evitar la duplicación de código y aun no tienen un contador de parte de la clase base…

    OriginalEl autor sad1raf

  10. 0

    Al menos con gcc 5.4 funciones virtuales podrían ser los miembros de la plantilla, sino a las propias plantillas.

    #include <iostream>
    #include <string>
    class first {
    protected:
    virtual std::string  a1() { return "a1"; }
    virtual std::string  mixt() { return a1(); }
    };
    class last {
    protected:
    virtual std::string a2() { return "a2"; }
    };
    template<class T>  class mix: first , T {
    public:
    virtual std::string mixt() override;
    };
    template<class T> std::string mix<T>::mixt() {
    return a1()+" before "+T::a2();
    }
    class mix2: public mix<last>  {
    virtual std::string a1() override { return "mix"; }
    };
    int main() {
    std::cout << mix2().mixt();
    return 0;
    }

    Salidas

    mix before a2
    Process finished with exit code 0

    OriginalEl autor Maxim Sinev

  11. 0

    En las otras respuestas a la propuesta de plantilla de función es una fachada y no ofrece ningún beneficio práctico.

    • Plantilla de funciones son útiles para la escritura de código sólo una vez que el uso de
      diferentes tipos.
    • Las funciones virtuales son útiles para tener una interfaz común para las diferentes clases.

    El lenguaje no permite virtual de la plantilla de funciones, pero con una solución es posible tener, por ejemplo, una plantilla de aplicación para cada clase y virtual de un interfaz común.

    Sin embargo, es necesario definir para cada tipo de plantilla de combinación de un maniquí virtual de la función de contenedor:

    #include <memory>
    #include <iostream>
    #include <iomanip>
    //---------------------------------------------
    //Abstract class with virtual functions
    class Geometry {
    public:
    virtual void getArea(float &area) = 0;
    virtual void getArea(long double &area) = 0;
    };
    //---------------------------------------------
    //Square
    class Square : public Geometry {
    public:
    float size {1};
    //virtual wrapper functions call template function for square
    virtual void getArea(float &area) { getAreaT(area); }
    virtual void getArea(long double &area) { getAreaT(area); }
    private:
    //Template function for squares
    template <typename T>
    void getAreaT(T &area) {
    area = static_cast<T>(size * size);
    }
    };
    //---------------------------------------------
    //Circle
    class Circle : public Geometry  {
    public:
    float radius {1};
    //virtual wrapper functions call template function for circle
    virtual void getArea(float &area) { getAreaT(area); }
    virtual void getArea(long double &area) { getAreaT(area); }
    private:
    //Template function for Circles
    template <typename T>
    void getAreaT(T &area) {
    area = static_cast<T>(radius * radius * 3.1415926535897932385L);
    }
    };
    //---------------------------------------------
    //Main
    int main()
    {
    //get area of square using template based function T=float
    std::unique_ptr<Geometry> geometry = std::make_unique<Square>();
    float areaSquare;
    geometry->getArea(areaSquare);
    //get area of circle using template based function T=long double
    geometry = std::make_unique<Circle>();
    long double areaCircle;
    geometry->getArea(areaCircle);
    std::cout << std::setprecision(20) << "Square area is " << areaSquare << ", Circle area is " << areaCircle << std::endl;
    return 0;
    }

    De salida:

    Cuadrado de área 1, área del Círculo es 3.1415926535897932385

    Probarlo aquí

    OriginalEl autor andreaplanet

Dejar respuesta

Please enter your comment!
Please enter your name here