¿Puede una plantilla de función de miembro de clase ser virtual?

304

He oído que las plantillas de funciones miembro de clase C ++ no pueden ser virtuales. ¿Es esto cierto?

Si pueden ser virtuales, ¿cuál es un ejemplo de un escenario en el que uno usaría tal función?

WannaBeGeek
fuente
12
Me enfrenté a un problema similar y también aprendí que es controvertido ser virtual y plantilla al mismo tiempo. Mi solución fue escribir la plantilla mágica que será común entre las clases derivadas y llamar a una función virtual pura que realiza la parte especializada. Por supuesto, esto está relacionado con la naturaleza de mi problema, por lo que podría no funcionar en todos los casos.
Tamás Szelei

Respuestas:

329

Las plantillas tienen que ver con el código de generación del compilador en tiempo de compilación . Las funciones virtuales tienen que ver con el sistema de tiempo de ejecución para determinar qué función llamar en tiempo de ejecución .

Una vez que el sistema de tiempo de ejecución descubrió que necesitaría llamar a una función virtual con plantilla, la compilación está lista y el compilador ya no puede generar la instancia adecuada. Por lo tanto, no puede tener plantillas de funciones miembro virtuales.

Sin embargo, existen algunas técnicas poderosas e interesantes derivadas de la combinación de polimorfismo y plantillas, en particular el llamado borrado de tipo .

sbi
fuente
32
No veo una razón de idioma para esto, solo razones de implementación . Las vtables no son parte del lenguaje, solo la forma estándar en que los compiladores implementan el lenguaje.
gerardw
16
Virtual functions are all about the run-time system figuring out which function to call at run-time- Lo siento, pero esta es una forma bastante incorrecta, y bastante confuso. Es solo indirección, y no hay un "tiempo de ejecución que se resuelva", se sabe durante el tiempo de compilación que la función a llamar es la señalada por el enésimo puntero en la tabla. "Resolver" implica que hay verificaciones 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- Se sabe si la función es virtual o no en tiempo de compilación.
dtech
99
@ddriver: 1. Si los compiladores ven void f(concr_base& cb, virt_base& vb) { cb.f(); vb.f(); }, entonces "sabe" qué función se invoca en el punto cb.f()llamado, y no lo sabe para vb.f(). Este último tiene que ser encontrado en tiempo de ejecución , por el sistema de tiempo de ejecución . Si desea llamar a esto "descifrar" y si esto es más o menos eficiente, no cambia un poco estos hechos.
sbi
99
@ddriver: 2. Las instancias de plantillas de funciones (miembro) son funciones (miembro), por lo que no hay ningún problema en absoluto al colocar un puntero a dicha instancia en la vtable. Pero las instancias de plantilla que se necesitan solo se conocen cuando se compila el llamador, mientras que las vtables se configuran cuando se compilan la clase base y las clases derivadas. Y todos estos se compilan por separado. Peor aún: las nuevas clases derivadas se pueden vincular a sistemas en ejecución en tiempo de ejecución (piense que su navegador carga un complemento de forma dinámica). Incluso el código fuente de la persona que llama puede perderse durante mucho tiempo cuando se crea una nueva clase derivada.
sbi
99
@sbi: ¿Por qué haces suposiciones basadas en mi nombre? No confundí genéricos y plantillas. Sé que los genéricos de Java son puramente ejecutados. No explicó exhaustivamente por qué no puede tener plantillas de funciones miembro virtuales en C ++, pero InQsitive sí. Simplificó demasiado la plantilla y la mecánica virtual para 'compilar tiempo' versus 'tiempo de ejecución' y concluyó que "no puede tener plantillas de funciones de miembro virtuales". Hice referencia a la respuesta de InQsitive, que hace referencia a "C ++ Templates The Complete Guide". No considero que sea "agitar las manos". Que tengas un buen día.
Javanator
133

Desde plantillas de C ++ La guía completa:

Las plantillas de funciones miembro no pueden declararse virtuales. Esta restricción se impone porque la implementación habitual del mecanismo de llamada de función virtual utiliza una tabla de tamaño fijo con una entrada por función virtual. Sin embargo, el número de instancias de una plantilla de función miembro no se fija hasta que se haya traducido todo el programa. Por lo tanto, el soporte de plantillas de funciones miembro virtuales requeriría soporte para un nuevo tipo de mecanismo en compiladores y enlazadores de C ++. Por el contrario, los miembros ordinarios de las plantillas de clase pueden ser virtuales porque su número se fija cuando se instancia una clase

InQusitive
fuente
8
Creo que el compilador y los enlazadores de C ++ de hoy en día, especialmente con el soporte de optimización del tiempo de enlace, deberían poder generar las vtables y las compensaciones requeridas en el momento del enlace. Entonces, ¿tal vez obtengamos esta función en C ++ 2b?
Kai Petzke
33

C ++ no permite funciones de miembro de plantilla virtual en este momento. La razón más probable es la complejidad de implementarlo. Rajendra da una buena razón por la que no se puede hacer en este momento, pero podría ser posible con cambios razonables del estándar. Especialmente determinar cuántas instancias de una función con plantilla existen realmente y construir la tabla virtual parece difícil si considera el lugar de la llamada a la función virtual. La gente de estándares solo tiene muchas otras cosas que hacer en este momento y C ++ 1x también es mucho trabajo para los redactores del compilador.

¿Cuándo necesitarías una función miembro con plantilla? Una vez me encontré con una situación en la que intenté refactorizar una jerarquía con una clase base virtual pura. Era un estilo pobre para implementar diferentes estrategias. Quería cambiar el argumento de una de las funciones virtuales a un tipo numérico y, en lugar de sobrecargar la función miembro y anular cada sobrecarga en todas las subclases, intenté usar funciones de plantilla virtual (y tuve que descubrir que no existen .)

pmr
fuente
55
@pmr: se puede invocar una función virtual desde un código que ni siquiera existía cuando se compiló la función. ¿Cómo determinaría el compilador qué instancias de una función miembro de plantilla virtual (teórica) generar para un código que ni siquiera existe?
sbi
2
@sbi: Sí, una compilación separada sería un gran problema. No soy un experto en compiladores de C ++, así que no puedo ofrecer una solución. Al igual que con las funciones con plantilla en general, se debe instanciar nuevamente en cada unidad de compilación, ¿verdad? ¿No resolvería eso el problema?
pmr
2
@sbi si se refiere a la carga dinámica de bibliotecas, ese es un problema general con las clases / funciones de plantilla, no solo con los métodos de plantilla virtual.
Roble
"C ++ no permite [...]" : agradecería ver una referencia a la norma (no importa si está actualizada cuando se escribió la respuesta o si está actualizada ocho años después) ...
Aconcagua
19

Tablas de funciones virtuales

Comencemos con algunos antecedentes sobre tablas de funciones virtuales y cómo funcionan ( fuente ):

[20.3] ¿Cuál es la diferencia entre cómo se llaman las funciones miembro virtuales y no virtuales?

Las funciones miembro no virtuales se resuelven estáticamente. Es decir, la función miembro se selecciona estáticamente (en tiempo de compilación) en función del tipo de puntero (o referencia) al objeto.

En contraste, las funciones miembro virtuales se resuelven dinámicamente (en tiempo de ejecución). Es decir, la función miembro se selecciona dinámicamente (en tiempo de ejecución) según el tipo de objeto, no el tipo de puntero / referencia a ese objeto. Esto se llama "enlace dinámico". La mayoría de los compiladores usan alguna variante de la siguiente técnica: si el objeto tiene una o más funciones virtuales, el compilador coloca un puntero oculto en el objeto llamado "puntero virtual" o "puntero v". Este puntero en v apunta a una tabla global llamada "tabla virtual" o "tabla en v".

El compilador crea una tabla v para cada clase que tiene al menos una función virtual. Por ejemplo, si la clase Circle tiene funciones virtuales para dibujar () y mover () y cambiar el tamaño (), habría exactamente una tabla v asociada con la clase Circle, incluso si hubiera un billón de objetos Circle, y el puntero v de cada uno de esos objetos Circle apuntaría a la v-table Circle. La tabla v en sí tiene punteros a cada una de las funciones virtuales de la clase. Por ejemplo, la v-table Circle tendría tres punteros: un puntero a Circle :: draw (), un puntero a Circle :: move () y un puntero a Circle :: resize ().

Durante el envío de una función virtual, el sistema de tiempo de ejecución sigue el puntero v del objeto a la tabla v de la clase, luego sigue la ranura correspondiente en la tabla v al código del método.

La sobrecarga del costo de espacio de la técnica anterior es nominal: un puntero adicional por objeto (pero solo para objetos que necesitarán realizar un enlace dinámico), más un puntero adicional por método (pero solo para métodos virtuales). La sobrecarga de costo de tiempo también es bastante nominal: en comparación con una llamada de función normal, una llamada de función virtual requiere dos recuperaciones adicionales (una para obtener el valor del puntero en V, una segunda para obtener la dirección del método). Nada de esta actividad de tiempo de ejecución ocurre con funciones no virtuales, ya que el compilador resuelve funciones no virtuales exclusivamente en tiempo de compilación en función del tipo de puntero.


Mi problema o como vine aquí

Estoy intentando usar algo como esto ahora para una clase base de cubefile con funciones de carga optimizadas con plantillas que se implementarán de manera diferente para diferentes tipos de cubos (algunos almacenados por píxel, otros por imagen, etc.).

Algún 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 que fuera, pero no se compilará debido a un combo virtual con plantilla:

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;

Terminé moviendo la declaración de plantilla al nivel de clase . Esta solución habría obligado a los programas a conocer los tipos específicos de datos que leerían antes de leerlos, lo cual es inaceptable.

Solución

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

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 las clases infantiles

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

Tenga en cuenta que LoadAnyCube no se declara en la clase base.


Aquí hay otra respuesta de desbordamiento de pila con una solución alternativa : necesita una solución de miembro de plantilla virtual .

Mark Essel
fuente
1
Encontré la misma situación, y la estructura de herencia de las clases masivas. las macros ayudaron.
ZFY
16

El siguiente código puede compilarse y ejecutarse correctamente, utilizando MinGW G ++ 3.4.5 en Windows 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 la salida es:

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

Y luego agregué una nueva clase X:

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

Cuando intenté usar la clase X en main () así:

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

g ++ informa el siguiente error:

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

Entonces es obvio que:

  • La función miembro virtual se puede utilizar en una plantilla de clase. Es fácil para el compilador construir vtable
  • Es imposible definir una función miembro de plantilla de clase como virtual, como puede ver, es difícil determinar la firma de la función y asignar entradas vtable.
Brent81
fuente
19
Una plantilla de clase puede tener funciones de miembro virtual. Una función miembro puede no ser tanto una plantilla de función miembro como una función miembro virtual.
James McNellis
1
en realidad falla con gcc 4.4.3. En mi sistema seguro Ubuntu 10.04
blueskin
3
Esto es totalmente diferente de lo que hizo la pregunta. Aquí toda la clase base tiene una plantilla. He compilado este tipo de cosas antes. Esto también se compilaría en Visual Studio 2010
ds-bos-msk
14

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 quiere hacer es tener una interfaz común y diferir la implementación a las subclases.

Tom
fuente
3
Esto se conoce como CRTP si alguien tiene curiosidad.
Michael Choi
1
Pero esto no ayuda para los casos en los que uno tiene una jerarquía de clases y quiere poder llamar métodos virtuales de punteros a las clases base. Su Foopuntero está calificado como Foo<Bar>, no puede apuntar a un Foo<Barf>o Foo<XXX>.
Kai Petzke
@KaiPetzke: No puede construir un puntero sin restricciones, no. Pero puede modelar cualquier código que no necesite conocer el tipo concreto, que tiene el mismo efecto (al menos conceptualmente, obviamente una implementación completamente diferente).
Tom
8

No, las funciones miembro de la plantilla no pueden ser virtuales.

Dirkgently
fuente
99
Mi curiosidad es: ¿por qué? ¿Qué problemas enfrenta el compilador al hacerlo?
WannaBeGeek
1
Necesita una declaración en el alcance (al menos, para obtener los tipos correctos). Es requerido por el estándar (y el idioma) tener una declaración en el alcance de los identificadores que usa.
Dirkgently
4

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

  • Las funciones de plantilla son útiles para escribir código solo una vez usando diferentes tipos.
  • Las funciones virtuales son útiles para tener una interfaz común para diferentes clases.

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

Sin embargo, es necesario definir para cada combinación de tipo de plantilla una función de contenedor virtual ficticio:

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

Salida:

El área cuadrada es 1, el área del círculo es 3.1415926535897932385

Pruébalo aquí

andreaplanet
fuente
3

Para responder a la segunda parte de la pregunta:

Si pueden ser virtuales, ¿cuál es un ejemplo de un escenario en el que uno usaría tal función?

Esto no es una cosa irrazonable querer 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 plantilla de función virtual es una función miembro que acepte un iterador genérico. O una función miembro que acepta un objeto de función genérico.

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

exclipy
fuente
66
Los genéricos de Java son azúcar sintáctica para la fundición. No son lo mismo que las plantillas.
Brice M. Dempsey
2
@ BriceM.Dempsey: Se podría decir que la conversión es la forma en que Java implementa los genéricos, en lugar de al revés ... y, de manera semántica, la presentación de casos de uso es válida IMO.
einpoklum
2

Existe una solución alternativa para el 'método de plantilla virtual' si se conoce de antemano un conjunto de tipos para el método de plantilla.

Para mostrar la idea, en el ejemplo a continuación solo se usan dos tipos ( inty double).

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

Solo se necesita implementar el método de plantilla TMethoden implementaciones derivadas ( AImpl, BImpl) y usar 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;
}

Salida:

0
1
2
3

NB: en Base::Methodrealidad es excedente para el código real ( VMethodpuede hacerse público y usarse directamente). Lo agregué para que parezca un método de plantilla 'virtual' real.

sad1raf
fuente
Se me ocurrió esta solución mientras resolvía un problema en el trabajo. Parece similar al anterior de Mark Essel, pero espero que esté mejor implementado y explicado.
sad1raf
Ya califico esto como ofuscación de código, y todavía no se da cuenta del hecho de que tiene que modificar la Baseclase original cada vez que necesita llamar a una función de plantilla con un tipo de argumento no compatible con los implementados hasta ahora. Evitar esta necesidad es la intención de las plantillas ...
Aconcagua
El enfoque de Essels es totalmente diferente: las funciones virtuales ordinarias que aceptan diferentes instancias de plantilla, y la función de plantilla final en la clase derivada solo sirve para evitar la duplicación de código y ni siquiera tiene una contraparte en la clase base ...
Aconcagua
2

Si bien una pregunta anterior que ha sido respondida por muchos, creo que un método sucinto, no muy diferente de los demás publicados, es utilizar una macro menor para ayudar a facilitar la duplicación de declaraciones de clase.

// abstract.h

// Simply define the types that each concrete class will use
#define IMPL_RENDER() \
    void render(int a, char *b) override { render_internal<char>(a, b); }   \
    void render(int a, short *b) override { render_internal<short>(a, b); } \
    // ...

class Renderable
{
public:
    // Then, once for each on the abstract
    virtual void render(int a, char *a) = 0;
    virtual void render(int a, short *b) = 0;
    // ...
};

Entonces, para implementar nuestra subclase:

class Box : public Renderable
{
public:
    IMPL_RENDER() // Builds the functions we want

private:
    template<typename T>
    void render_internal(int a, T *b); // One spot for our logic
};

El beneficio aquí es que, al agregar un tipo recientemente admitido, todo se puede hacer desde el encabezado abstracto y renunciar posiblemente a la rectificación en múltiples archivos de origen / encabezado.

mccatnm
fuente
0

Al menos con gcc 5.4, las funciones virtuales podrían ser miembros de plantilla, pero tienen que ser plantillas en sí mismas.

#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
Maxim Sinev
fuente
0

Prueba esto:

Escribe en classeder.h:

template <typename T>
class Example{
public:
    T c_value;

    Example(){}

    T Set(T variable)
    {
          return variable;
    }

    virtual Example VirtualFunc(Example paraM)
    {
         return paraM.Set(c_value);
    }

Marque, si trabaja con esto, para escribir este código en main.cpp:

#include <iostream>
#include <classeder.h>

int main()
{
     Example exmpl;
     exmpl.c_value = "Hello, world!";
     std::cout << exmpl.VirtualFunc(exmpl);
     return 0;
}
GobeRadJem32
fuente