2011-03-20 19 views
44

El siguiente fragmento produce una "llamada ambigua a foo" error durante la compilación, y me gustaría saber si hay alguna manera de evitar este problema sin calificar totalmente la llamada a foo:¿Por qué las funciones heredadas múltiples con el mismo nombre pero diferentes firmas no se tratan como funciones sobrecargadas?

#include <iostream> 

struct Base1{ 
    void foo(int){ 
    } 
}; 

struct Base2{ 
    void foo(float){ 
    } 
}; 

struct Derived : public Base1, public Base2{ 
}; 

int main(){ 
    Derived d; 
    d.foo(5); 

    std::cin.get(); 
    return 0; 
} 

Por lo tanto, la pregunta es como dice el título. Ideas? Quiero decir, los siguientes trabajos sin problemas:

#include <iostream> 

struct Base{ 
    void foo(int){ 
    } 
}; 

struct Derived : public Base{ 
    void foo(float){ 
    } 
}; 

int main(){ 
    Derived d; 
    d.foo(5); 

    std::cin.get(); 
    return 0; 
} 
+2

agregue declaraciones de registro dentro de los dos foo (en el segundo caso) a la constante que se llama función, se sorprenderá ... C++ está lleno de regla arcana;) –

+1

@Matthieu: * jadeo *! Malditas reglas para esconderse. :( – Xeo

Respuesta

42

reglas de búsqueda miembros se definen en la Sección 10,2/2

Los siguientes pasos definen el resultado de búsqueda de nombre en un ámbito de clase, C. Primero, se considera cada declaración para el nombre en la clase y en cada uno de sus sub-objetos de clase base. Un nombre de miembro f en un subobjeto B oculta un nombre de miembro f en un subobjeto A si A es un subobjeto de clase base de B. Cualquier declaración que esté tan oculta se elimina de la consideración. Cada una de estas declaraciones que se introdujo mediante una declaración de uso se considera procedente de cada subobjeto de C que es del tipo que contiene la declaración designada por la declaración de uso. Si el conjunto resultante de declaraciones no son todas de subobjetos del mismo tipo, o el conjunto tiene un miembro no estático e incluye miembros de distintos subobjetos, existe una ambigüedad y el programa está mal formado. De lo contrario, ese conjunto es el resultado de la búsqueda.

class A { 
public: 
    int f(int); 

}; 
class B { 
public: 
    int f(); 

}; 
class C : public A, public B {}; 
int main() 
{ 
    C c; 
    c.f(); // ambiguous 
} 

lo que puede utilizar las declaraciones usingA::f y B::f para resolver esta ambigüedad

class C : public A, public B { 
    using A::f; 
    using B::f; 

}; 

int main() 
{ 
    C c; 
    c.f(); // fine 
} 

El segundo código funciona a la perfección, porque void foo(float) está dentro del alcance C. En realidad, d.foo(5); llama al void foo(float) y no a la versión int.

+2

La versión 'void foo (float)' se llama realmente me consiguió allí .. gracias por su amplia respuesta. :) – Xeo

+1

Una cosa que viene a la mente ... ¿hay una situación, donde uno querría ocultar el funciones de la clase base si tienen diferentes firmas? Para las mismas funciones de firma, claro, eso es útil, pero para otras no puedo imaginar un buen ejemplo ... – Xeo

+0

+1. Muy agradable. Yo no sabía esto. Gracias Prasoon. :-) – Nawaz

2

¿Funcionará para usted?

struct Derived : public Base1, public Base2{ 
    using Base2::foo;} 
2

Buscar nombre es una fase separada para resolución de sobrecarga.

La búsqueda de nombres se produce primero. Ese es el proceso de decidir a qué alcance se aplica el nombre. En este caso, debemos decidir si d.foo significa d.D::foo, o d.B1::foo, o d.B2::foo. Las reglas de búsqueda de nombre no tienen en cuenta los parámetros de la función ni nada; es puramente sobre nombres y alcances.

Solo una vez que se ha tomado esa decisión, entonces realizamos una resolución de sobrecarga en las diferentes sobrecargas de la función en el ámbito donde se encontró el nombre.

En su ejemplo, al llamar al encontraría D::foo() si existiera dicha función. Pero no hay ninguno. Entonces, trabajando hacia atrás en los ámbitos, prueba las clases base. Ahora foo podría buscar hasta B1::foo o B2::foo, por lo que es ambiguo.

Por la misma razón, obtendría ambigüedad llamando sin calificar foo(5); dentro de una función de miembro D.


El efecto de la solución recomendada:

struct Derived : public Base1, public Base2{ 
    using Base1::foo; 
    using Base2::foo; 

es que esto crea el nombre D::foo, y lo hace a identificar dos funciones. El resultado es que d.foo se resuelve en d.D::foo, y luego puede ocurrir una resolución de sobrecarga en estas dos funciones que están identificadas por D::foo.

Nota: En este ejemplo, D::foo(int) y Base1::foo(int) son dos identificadores para una función; pero en general, para el proceso de búsqueda de nombre y resolución de sobrecarga, no importa si son dos funciones separadas o no.

Cuestiones relacionadas