2011-02-10 11 views
11

Dado el siguiente código, el compilador muestra un mensaje que indica que error: templates may not be ‘virtual’. ¿Alguien tiene alguna sugerencia sobre cómo resolver el error?las plantillas pueden no ser "virtuales"

template < class FOO_TYPE> 
class CFoo{ 
    public: 
     ... 
     template < class BAR_TYPE > 
     virtual void doSomething(const CBar<BAR_TYPE> &); // here's the error 
     ... 
     virtual ~CFoo(); 
    protected: 
     MyClass <FOO_TYPE> * m_pClass; 
}; 

template < class FOO_TYPE > 
template < class BAR_TYPE > 
void CFoo<FOO_TYPE>::doSomething(const CBar<BAR_TYPE> & refBar){ 
    ... 
} 
+5

no es un error, es una característica, no se puede declarar una plantilla de función que sea virtual. Necesita explorar otro enfoque, y eso depende de lo que esté tratando de hacer ... – Nim

+0

El hecho es que el parámetro de la función es un objeto de plantilla y no puedo cambiar su declaración. – Javier

+0

¿por qué necesita una función diferente para cada instancia de CBar? –

Respuesta

16

La razón más fácil para ver por qué esto es ilegal es considerar el vtable. Claro, eso es solo una implementación común, y otros están permitidos. Pero todas las funciones de virtual en C++ están diseñadas de tal manera que se pueden implementar con un vtable.

Ahora, ¿cuántas entradas hay en el vtable de CFoo<int>? ¿Hay una entrada para doSomething<float>? ¿Y doSomething<float*>? ¿Y doSomething<float**>? Las plantillas como estas permiten generar un conjunto infinito de funciones. Por lo general, eso no es problema, ya que solo se usa un subconjunto finito, pero para las funciones virtuales este subconjunto no se conoce, y por lo tanto, el valor de la tabla debe ser infinito.

Ahora, es posible que realmente quisiera solo una entrada en el vtable. En ese caso, tendría que escribir de la siguiente manera:

template < class FOO_TYPE, class BAR_TYPE> 
class CFoo{ 
    public: 
     ... 
     virtual void doSomething(const CBar<BAR_TYPE> &); // now OK. 
     ... 
     virtual ~CFoo(); 
    protected: 
     MyClass <FOO_TYPE> * m_pClass; 
}; 

Esto significa que la viable para CFoo<int, float> tendrá una entrada, por doSomething(float const&).

+1

Podría conocerse en la etapa de enlace después de compilar todas las unidades de compilación. El estándar es un poco rápido en descartar aquí. –

+3

@ v.oddou: No es realista. El enlazador debería hacer coincidir todas las llamadas virtuales con todas las posibles clases base y crear instancias de las plantillas. Esas instancias entonces necesitan ser compiladas. Esas nuevas instancias a su vez pueden contener nuevas llamadas virtuales, por lo que este proceso debería ser iterativo. – MSalters

1

Bueno, el mensaje de error es bastante claro. Member function templates can't be virtual. Cómo resolver esto depende de su problema, pero lo más fácil sería hacer que las funciones miembro no sean virtuales y reconsiderar su diseño.

+0

gracias. En mi caso, necesito tener este "refBar" como parámetro y pertenece a una clase de plantilla. – Javier

+1

¿Sabes cuántos parámetros de plantilla diferentes habrá? 3? 8? ¿Puedes sobrecargar la función para cada uno de esos? Si no sabe, ¿cómo sabrá el compilador cuántas funciones virtuales hay? –

+0

@Bo: solo 2 parámetros: FOO_TYPE y BAR_TYPE – Javier

1

Si realmente necesita hacer que este método sea virtual, considere la posibilidad de hacerpolimórfico y pasar un tipo de base en el que no esté templado.

EDIT: algo como esto:

// non-templated base class 
class BarBase 
{ 
// common methods go here.. 
}; 

template <typename BAR_TYPE> 
class CBar : public BarBase 
{ 
// implement methods from BarBase ... 
}; 

template < class FOO_TYPE> 
class CFoo{ 
    public: 
     ... 
     // now we take the base type, and this method does not need to be a template 
     virtual void doSomething(BarBase const* ptrBar); 
     ... 
     virtual ~CFoo(); 
    protected: 
     MyClass <FOO_TYPE> * m_pClass; 
}; 

template < class FOO_TYPE > 
void CFoo<FOO_TYPE>::doSomething(BarBase const* ptrBar){ 
.. 
} 
+0

lo siento, no entendí eso. ¿te importa hacer un ejemplo? – Javier

1

Puede utilizar lo que llamamos en Symbian como "patrón de diseño de la plantilla". Aquí hay un código de muestra para que tenga una idea:

class Base { 
public: 
     virtual int DoSomething() = 0; 
protected: 
     Base(); 
}; 

class IntermediateBase : public Base { 
protected: 
     IntermediateBase(void* aSomeParam, void* aArg) 
     : iSomeParam(aSomeParam) 
     , iArgs(aArg) 
     {} 

     virtual int DoSomething() = 0; 
protected: 
     void* iSomeParam; 
     void* iArgs; 
}; 

template <class TYPE, class INPUT> 
class ConcreteClass : public IntermediateBase { 
     typedef int (TYPE::*MemberFuncPtr)(const INPUT&); 
public: 
     ConcreteClass(TYPE& aCommandType, 
         INPUT& aArgumentsToCommand, 
         MemberFuncPtr aMFP) 
     : IntermediateBase(static_cast<TYPE*>(&aCommandType), 
          static_cast<INPUT*>(&aArgumentsToCommand)) 
     , iMFP(aMFP) 
     {} 

     virtual int DoSomething() // VIRTUAL AND INLINE Note - dont make it 
            // virtual and inline in production if 
            // possible to avoid out-of-line copy 
     { 
      return static_cast<TYPE*>(iSomeParam)->*ConcreteClass::iMFP) 
          (*(static_cast<INPUT*>(iArgs)); 
     } 
private: 
     MemberFuncPtr iMFP; 
}; 
+15

El formateo me hace querer lastimar algo. – GManNickG

+0

Disculpa, lo formateé ahora. – Viren

+0

gracias por la ilustración del código. Definí el método 'doSomething' como' plantilla' debido a su argumento. Solo hay dos tipos de plantilla, la correspondiente a 'CFoo' y la otra a' CBar'. Creo que al declarar 'CFoo ' como una clase de plantilla doble debería estar bien. ¿Qué piensas? – Javier

Cuestiones relacionadas