¿Advertencia de función virtual sobrecargada de c ++ por clang?

80

clang emite una advertencia al compilar el siguiente código:

struct Base
{
    virtual void * get(char* e);
//    virtual void * get(char* e, int index);
};

struct Derived: public Base {
    virtual void * get(char* e, int index);
};

La advertencia es:

warning: 'Derived::get' hides overloaded virtual function [-Woverloaded-virtual]

(la advertencia mencionada debe estar habilitada, por supuesto).

No entiendo por qué. Tenga en cuenta que descomentar la misma declaración en Base cierra la advertencia. Tengo entendido que dado que las dos funciones get () tienen firmas diferentes, no se puede ocultar.

¿Es cierto clang? ¿Por qué?

Tenga en cuenta que esto está en MacOS X, que ejecuta una versión reciente de Xcode.

clang --version
Apple LLVM version 5.0 (clang-500.1.74) (based on LLVM 3.3svn)

Actualización: mismo comportamiento con Xcode 4.6.3.

Jean-Denis Muys
fuente

Respuestas:

115

Esta advertencia está ahí para evitar el ocultamiento accidental de sobrecargas cuando se pretende anular. Considere un ejemplo ligeramente diferente:

struct chart; // let's pretend this exists
struct Base
{
    virtual void* get(char* e);
};

struct Derived: public Base {
    virtual void* get(chart* e); // typo, we wanted to override the same function
};

Como es una advertencia, no significa necesariamente que sea un error, pero podría indicar uno. Por lo general, tales advertencias tienen un medio para apagarlas siendo más explícitas y dejando que el compilador sepa que usted tuvo la intención de lo que escribió. Creo que en este caso puedes hacer lo siguiente:

struct Derived: public Base {
    using Base::get; // tell the compiler we want both the get from Base and ours
    virtual void * get(char* e, int index);
};
R. Martinho Fernandes
fuente
11
Se podría señalar que esta solución para "apagar localmente la advertencia" también está cambiando la semántica del código: ahora puede invocar el getmiembro de la función con un solo argumento en un objeto de tipo estático Derived. Sin la declaración using, lo mismo conduciría a un error de compilación.
Anuncio N
29

Otra forma de deshabilitar la advertencia manteniendo intacta la interfaz pública de la estructura sería:

struct Derived: public Base
{
    virtual void * get(char* e, int index);
private:
    using Base::get;
};

Esto no permite que un consumidor de Derivedllame Derived::get(char* e)mientras silencia la advertencia:

Derived der;
der.get("", 0); //Allowed
der.get("");    //Compilation error
Pedro
fuente
2
¡Esta es definitivamente una forma segura de eliminar esta advertencia cuando planeaba reemplazar el getmétodo de clase de la base !
jpo38
Sin embargo, tenga cuidado con los casos en los que esto causaría una llamada ambigua. Entonces, esta solución tampoco es 100% segura.
sigy
22

La solución de R. Martinho Fernandes es perfectamente válida si realmente desea llevar el get()método tomando un solo argumento char * enDerived alcance.

En realidad, en el fragmento que proporcionó, no hay necesidad de métodos virtuales (ya que Base y Derived no comparten ningún método con la misma firma).

Suponiendo que realmente existe una necesidad de polimorfismo, el comportamiento de ocultación podría ser, no obstante, lo que se pretende. En este caso, es posible deshabilitar localmente la advertencia de Clang, con el siguiente pragma:

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Woverloaded-virtual"
    // Member declaration raising the warning.
#pragma clang diagnostic pop
Anuncio N
fuente
1
Esta respuesta fue doblemente asombrosa. Al principio fue la respuesta exacta a lo que estaba buscando, que "quería que mi método estuviera ahí". Mientras escribía un comentario en mi código por la razón del pragma y lo estúpido que era el sonido metálico, mis ojos captaron que escribí anular, pero la advertencia fue sobrecargada. Luego hice clic y me di cuenta de que me olvidé constdel método heredado y el sonido metálico estuvo bien todo el tiempo. En caso de duda, confíe en el compilador. Cuando dude del compilador, confíe en el compilador. :) +1 por darme tanto lo que buscaba como lo que necesitaba.
Nevelis
17

Advertencia significa que no habrá función void * get (char * e) en el alcance de la clase Derived, porque está oculta por otro método con el mismo nombre. El compilador no buscará funciones en las clases base si la clase derivada tiene al menos un método con el nombre especificado, incluso si tiene otros argumentos.

Este código de muestra no se compilará:

class A
{
public:
    virtual void Foo() {}
};

class B : public A
{
public:
    virtual void Foo(int a) {}
};


int main()
{
    B b;
    b.Foo();
    return 0;
}
Agujero De GusanoMago
fuente
1
Eso es un buen punto: escondite está realmente activa ocurriendo, a pesar de las diferentes firmas deberían ser suficientes para prevenirlo.
Jean-Denis Muys
Mi definición de ocultación es tener la misma firma pero no anular ... lo cual no es el caso aquí.
NGauthier
La solución para evitar esconderse, de C ++ en pocas palabras : "Inserte una declaración using en la clase derivada si desea que el compilador considere las funciones de la clase base como candidatas", como se muestra en otras respuestas.
qris
Si también incluye una solución (usando ...), esta debería ser la respuesta aceptada, ya que es la única que explica correctamente lo que sucede y por qué esta es una advertencia válida
MikeMB
1
Creo que esta es la respuesta más clara. Sin embargo, vale la pena señalar que todavía puede llamar b.Foo();. Solo necesitas escribir b.A::Foo();.
Timmmm