Resolución de sobrecarga de C++ [duplicar]


Esta pregunta ya tiene una respuesta aquí:

Dado el siguiente ejemplo, ¿por qué tengo que usar explícitamente la instrucción b->A::DoSomething() en lugar de solo b->DoSomething()?

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

Estoy usando Microsoft VS 2005. (Nota: usar 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;
}
Author: Peter Mortensen, 2008-09-16

9 answers

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

Una solución sería bajar la sobrecarga de A al alcance de B:

class B : public A {
public:
    using A::DoSomething;
    // …
}
 40
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 13:18:27

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

Básicamente el compilador encuentra una coincidencia de nombre "doSomething(int)" en el ámbito de B, ve que los parámetros no coinciden y se detiene con un error.

Se puede superar usando el 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;
}
 14
Author: Pieter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 13:18:43

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

Para sortearlo, necesita decirle al compilador a qué método desea llamar colocando a using A::doSomething en la clase B.

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

 5
Author: Lehane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 13:27:02

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 de clase A:

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

Ahora de repente:

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

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

 5
Author: James Curran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-21 16:03:17

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

3.3.7 Ocultación de nombres [basic.alcance.ocultar]

...[snip]...

3 En una definición de función miembro, la declaración de un nombre local se oculta el declaración de un miembro de la clase con el mismo nombre; véase básico.alcance.clase. Declaración de un miembro de una clase derivada (clase.derivado ) oculta la declaración de un miembro de una clase base de el mismo nombre; ver clase.miembro.lookup .

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

 3
Author: Arkadiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 13:30:17

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, entonces 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 declaración using:

class B : public A  
{  
  public:  
    int DoSomething(int x) {return 1;};  
    using A::DoSomething;
};  
 2
Author: Anthony Williams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 13:19:23

Eso no es sobrecargar! ¡Eso es ESCONDERSE!

 2
Author: ugasoft,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-23 18:22:56

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

class B : public A  
{  
  public:
    using A::DoSomething;
    int DoSomething(int x) {return 1;};  
}; 
 1
Author: Jono,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 13:19:24

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 ();

 1
Author: slicedlime,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-16 13:20:37