2010-11-11 15 views
7

considerar:¿Sobrescribe un método virtual no const ocultar una sobrecarga const?

#include <iostream> 

using namespace std; 

struct A { 
    virtual void f() { cout << "A::f" << endl; } 
    virtual void f() const { cout << "A::f const" << endl; } 
}; 

struct B : public A {}; 

struct C : public A { 
    virtual void f() { cout << "C::f" << endl; } 
}; 


int main() 
{ 
    const B b; 
    b.f(); // prints "A::f const" 

    const C c; 
    c.f(); 
    // Compile-time error: passing ‘const C’ as ‘this’ argument of 
    // ‘virtual void C::f()’ discards qualifiers 
} 

(. Estoy usando GCC)

Así que parece que la versión const de f() se oculta en C. Esto tiene mucho sentido para mí, pero es ¿ordenado por el estándar?

+4

"Virtual" es una pista falsa. No estamos llamando a ninguna 'f' de forma virtual (a través de un puntero o referencia de clase base) aquí. Todas las búsquedas de 'f' encuentran la' f' más derivada. – MSalters

+0

Virtual y const no se aplican realmente a la pregunta, pero los he dejado como etiquetas ya que no veo mucho daño y no es necesario incluir una etiqueta más relevante. –

+2

Estoy de acuerdo con 'virtual', pero' const' es de lo que se trata la pregunta. Reemplazando 'f()' oculta 'f() const'. – Ari

Respuesta

4

lo haré (una vez más) enlace de este gran article:

En primer lugar, [compilador] se ve en el ámbito inmediato, en este caso el alcance de la clase C , y hace una lista de todas las funciones que puede encontrar son llamadas f (independientemente de si están accesibles o incluso toman el número de parámetros correctos). Sólo si no qué luego continuar "hacia afuera" en la próxima encerrando alcance [...]

Así que sí, la versión de constf se oculta, y eso es perfectamente normal. Como señaló Simone, puede usar una declaración using para traer A::f en el alcance C.

+0

+1 Buen artículo. Como punto de interés, esto también se discute en el Ítem 33 de Effective C++ Third Edition –

2

Insertar using B::f;

struct C : public A { 
    using A::f; 
    virtual void f() { cout << "C::f" << endl; } 
}; 

C++ estándar 2003. 13.2 p.1:

Dos declaraciones de función del mismo nombre se refieren a la misma función si están en el mismo alcance y tener declaraciones de parámetros equivalentes (13.1). Una función miembro de una clase derivada no es en el mismo ámbito como un miembro de función del mismo nombre en una clase base.

Así C::f, oculta todos A::f.

+2

Thid no compila. Tal vez te refieres a "A :: f". – Simone

+0

Lo siento. Por supuesto, A :: f. –

3

Sí, lo es. Usted puede escribir:

struct C : public A { 
    virtual void f() { cout << "C::f" << endl; } 
    using A::f;  
}; 

Para que el código de compilación:

int main() 
{ 
    const B b; 
    b.f(); // prints "A::f const" 

    const C c; 
    c.f(); // prints "A::f const" 
} 

Para más informaciones, se puede hacer referencia al proyecto de documento 2010 C++ (que se puede encontrar here) capítulo 10.2 (3-. 4).

+0

+1 para la referencia al estándar, pero obtuve más información de la respuesta de icecrime. Estaba muy consciente de la opción 'usar'. No estaba interesado en hacer esta compilación, sino en comprender el lenguaje. – Ari

+0

Gracias Ari, pero recuerde que su pregunta puede ser consultada por personas con el mismo problema que también desean compilarla. Es mejor escribir algo más que algo menos, ¿estás de acuerdo? :) – Simone

3

No es la virtualidad o const-ness (o la falta de ella) que oculta el miembro base, cualquier método derivado oculta un método base con el mismo nombre. Esto se hizo para mejorar el problema de la clase base frágil.

Imagine que su código funcionaba (posiblemente durante años) como abajo, con partes no relevantes Eliminado:

struct Base { 
}; 

struct Derived : Base { 
    void f(double); 
} 

void g(Derived &d) { 
    d.f(42); 
} 

Luego hay que modificar la base para incluir un método que hace algo completamente diferente, pero, por alguna razón, usted quiere nombrarlo 'f':

struct Base { 
    void f(int); 
}; 

Sin esta regla, cada uso de un f llamando Derivado necesita ser evaluado de forma manual - y si la base está en una biblioteca dada a otras personas, es posible que ni siquiera tener acceso a esos otros usos! Se agrava frente a las conversiones definidas por el usuario (implícitas).

En su lugar, se decidió requerir que las clases derivadas indiquen explícitamente que desean importar nombres dados de Base con una declaración using. Esta regla puede ser sorprendente y no estoy seguro de que sea un beneficio neto para el idioma de hoy, pero no me preguntaron; en ese momento, probablemente solo podría haberles respondido con palabras de dos sílabas, de todos modos. :)

Cuestiones relacionadas