Resolución de sobrecarga de C ++

Dado el siguiente ejemplo, ¿por qué tengo que usar explícitamente la statement b->A::DoSomething() lugar de simplemente b->DoSomething() ?

¿No debería la resolución de sobrecarga del comstackdor averiguar de qué método estoy hablando?

Estoy usando Microsoft VS 2005. (Nota: el uso de Virtual no ayuda en este caso).

 class A { public: int DoSomething() {return 0;}; }; class B : public A { public: int DoSomething(int x) {return 1;}; }; int main() { B* b = new B(); b->A::DoSomething(); //Why this? //b->DoSomething(); //Why not this? (Gives compiler error.) delete b; return 0; } 

Las dos “sobrecargas” no están en el mismo scope. De forma predeterminada, el comstackdor solo considera el scope del nombre más pequeño posible hasta que encuentra una coincidencia de nombre. La coincidencia de argumentos se realiza después . En su caso, esto significa que el comstackdor ve B::DoSomething . Luego intenta hacer coincidir la lista de argumentos, que falla.

Una solución sería reducir la sobrecarga de A en el scope de B :

 class B : public A { public: using A::DoSomething; // … } 

La resolución de sobrecarga es una de las partes más feas de C ++

Básicamente, el comstackdor encuentra un nombre que coincide con “DoSomething (int)” en el scope de B, ve que los parámetros no coinciden y se detiene con un error.

Se puede superar mediante el uso de A :: DoSomething en la clase B

 class A { public: int DoSomething() {return 0;} }; class B : public A { public: using A::DoSomething; int DoSomething(int x) {return 1;} }; int main(int argc, char** argv) { B* b = new B(); // b->A::DoSomething(); // still works, but... b->DoSomething(); // works now too delete b; return 0; } 

No, este comportamiento está presente para garantizar que no te atrapen heredando de clases base distantes por error.

Para evitarlo, debe decirle al comstackdor a qué método desea llamar colocando un A usando DoSomething en la clase B.

Vea este artículo para una descripción rápida y fácil de este comportamiento.

La presencia de un método en una clase derivada oculta todos los métodos con el mismo nombre (independientemente de los parámetros) en las clases base. Esto se hace para evitar problemas como este:

 class A {} ; class B :public A { void DoSomething(long) {...} } B b; b.DoSomething(1); // calls B::DoSomething((long)1)); 

que más tarde alguien cambia la clase A:

 class A { void DoSomething(int ) {...} } 

ahora de repente:

 B b; b.DoSomething(1); // calls A::DoSomething(1); 

En otras palabras, si no funcionara así, un cambio no relacionado en una clase que no controlas (A) podría afectar silenciosamente cómo funciona tu código.

Esto tiene algo que ver con la forma en que funciona la resolución de nombres. Básicamente, primero encontramos el scope del que proviene el nombre, y luego recostackmos todas las sobrecargas para ese nombre en ese scope. Sin embargo, el scope en su caso es clase B, y en clase B, B :: DoSomething oculta A :: DOSomething:

3.3.7 Ocultar nombre [basic.scope.hiding]

…[recorte]…

3 En una definición de función miembro, la statement de un nombre local oculta la statement de un miembro de la clase con el mismo nombre; ver basic.scope.class . La statement de un miembro en una clase derivada ( class.derived ) oculta la statement de un miembro de una clase base del mismo nombre; vea class.member.lookup .

Debido a la ocultación de nombres, A :: DoSomething ni siquiera se considera para la resolución de sobrecarga

Cuando define una función en una clase derivada, oculta todas las funciones con ese nombre en la clase base. Si la función de clase base es virtual y tiene una firma compatible, la función de clase derivada también anula la función de clase base. Sin embargo, eso no afecta la visibilidad.

Puede hacer visible la función de clase base con una statement using:

 class B : public A { public: int DoSomething(int x) {return 1;}; using A::DoSomething; }; 

¡Eso no es una sobrecarga! ¡Eso es OCULTAR!

Al buscar el árbol de herencia para la función a usar, C ++ usa el nombre sin argumentos, una vez que ha encontrado cualquier definición, se detiene y luego examina los argumentos. En el ejemplo dado, se detiene en la clase B. Para poder hacer lo que está buscando, la clase B debe definirse así:

 class B : public A { public: using A::DoSomething; int DoSomething(int x) {return 1;}; }; 

La función está oculta por la función con el mismo nombre en la subclase (pero con una firma diferente). Puede mostrarlo usando la instrucción using, como al usar A :: DoSomething ();