2012-06-07 10 views
12

Suponiendo que tenía dos clases, la primera para escribir tipos primitivos (bool, int, float, etc.) y la segunda que se extiende el primero en escribir también los tipos complejos:¿Por qué C++ prefiere este método de plantilla a una sobrecarga de método?

struct Writer { 
    virtual void Write(int value) = 0; 
}; 

struct ComplexWriter : public Writer { 
    template <typename TValue> void Write(const TValue &value) { 
     boost::any any(value); 
     Write(any); 
    } 
    //virtual void Write(int value) = 0; // see question below 
    virtual void Write(const boost::any &any) = 0; 
}; 

La idea es que si alguien llama al myWriter.Write(someIntValue);, la sobrecarga int recibirá prioridad sobre el método de plantilla.

En cambio, mi compilador (Visual C++ 11.0 RC) siempre elige el método de la plantilla. El siguiente fragmento de código, por ejemplo, se imprimirá Wrote any a la consola:

struct ComplexWriterImpl : public ComplexWriter { 
    virtual void Write(int value) { std::cout << "Wrote an int"; } 
    virtual void Write(const boost::any &any) { std::cout << "Wrote any"; } 
}; 

void TestWriter(ComplexWriter &writer) { 
    int x = 0; 
    writer.Write(x); 
} 

int main() { 
    ComplexWriterImpl writer; 
    TestWriter(writer); 
} 

El comportamiento cambia repentinamente cuando declaro el método Write(int) en la clase ComplexWriter también (véase la línea comentada en el primer fragmento). A continuación, imprime Wrote an int en la consola.

¿Es así como se debe comportar mi compilador? ¿El estándar C++ dice explícitamente que solo las sobrecargas definidas en la misma clase (y no una clase base) se priorizarán sobre un método con plantilla?

+2

No veo donde se sobrecarga un método de toma de int con una plantilla de. Tienes dos clases no relacionadas, 'Writer' con un método que toma' int' y 'ComplexWriter' con un método de plantilla (y un método que toma' boost :: any const & '). En su código de prueba utiliza 'ComplexWriter', así que por supuesto llamará a un miembro de' ComplexWriter'. Si realmente esperaba que llamara a un miembro de la clase "Escritor" sin relación alguna, ¿podría explicar por qué (y cómo) debería hacerlo? – celtschk

+2

Menciona que 'ComplexWriter' extiende' Writer', pero en el código que enumeró no hereda de 'Writer'. ¿Es esta la configuración correcta? – Attila

+0

Gracias por señalar eso. Sí, se suponía que ComplexWriter derivaba de Writer. Actualicé la pregunta en consecuencia. El resultado sigue siendo el mismo (aunque, por supuesto, se olvidó de especificar que la clase base se conecta al núcleo de mi pregunta. Outch). – Cygon

Respuesta

7

El problema es que en el punto que está llamando writer.Write(x) el compilador ve no es un ComplexWriter un ComplexWriterImpl, por lo que sólo es consciente de las funciones definidas en ComplexWriter - la función de plantilla y la función boost::any.

ComplexWriter no contiene funciones virtuales que permiten un banco int, por lo que no tiene forma de llamar a través de la sobrecarga int definido en ComplexWriterImpl

Cuando se agrega en la sobrecarga virtual para la clase ComplexWriter, entonces el compilador se da cuenta de que hay una sobrecarga de enteros en la clase ComplexWriter por consiguiente, a través de su implementación en ComplexWriterImpl

EDIT: Ahora que has editado en la herencia entre ComplexWriter & escritor, tengo una explicación más completa de usted:

Cuando crea una subclase y define una función en ella, entonces se ocultan todas las funciones de ese nombre en la clase base, independientemente de sus tipos de argumentos.

Usted puede evitar esto con el uso de palabras clave que creen:

struct ComplexWriter : public Writer { 
    template <typename TValue> void Write(const TValue &value) { 
     boost::any any(value); 
     Write(any); 
    } 
    using Writer::Write; 
    virtual void Write(const boost::any &any) = 0; 
}; 

Para más detalles ver esta FAQ: http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9

EDIT 2: Sólo para confirmar que esto realmente a resolver su problema: http://ideone.com/LRb5a

+0

Sí, olvidé especificar la clase base en mi fragmento. ComplexWriter hereda de Writer, pero el comportamiento que estoy observando es como lo expliqué, incluso con la clase base correctamente especificada. – Cygon

+0

@Cygon Sí, se dio cuenta de eso. Solo estoy tratando de encontrar una explicación/edición en este momento. ¡Aunque puedo terminar borrando esta respuesta si fallo! – obmarg

+0

¡Gracias por la respuesta rápida en cualquier caso! Olvidar especificar la clase base fue realmente un error tonto, ya que sesgó toda la pregunta :) – Cygon

2

al acceder al objeto a través de la "interfaz" ComplexWriter, el compilador intentará resolver la llamada de función a Write(int) utilizando las definiciones de esa clase. Si no puede hacerlo, considerará las clases base.

En este caso, tiene dos candidatos: Write(any) y la versión con plantilla. Dado que no hay ningún Write(int) explícito disponible en este punto, tendrá que elegir entre estas dos opciones.Write(any) requiere una conversión implícita, mientras que la versión con plantilla no, por lo que se llama a la versión con plantilla (que a su vez llama a Write(any)).

Para hacer el Write(int) de Writer disponibles, importar los Writer::Write funciones:

class ComplexWriter : public Writer 
{ 
    using Writer::Write; 
    // rest is as before 
}; 
Cuestiones relacionadas