2009-08-21 12 views
9

Estoy recibiendo el siguiente error de compilación en una de mis clases, usando gcc 3.4.5 (MinGW):solicitud de miembro de `...' es ambiguo en g ++

src/ModelTester/CModelTesterGui.cpp:1308: error: request for member `addListener' is ambiguous 
include/utility/ISource.h:26: error: candidates are: void utility::ISource<T>::addListener(utility::IListener<T>*) [with T = const SConsolePacket&] 
include/utility/ISource.h:26: error:     void utility::ISource<T>::addListener(utility::IListener<T>*) [with T = const SControlPacket&] 

Esperemos que se puede ver que ISource<T> es una interfaz de plantilla que simplemente indica que el objeto puede ser un informante para un objeto que es de algún tipo coincidente IListener<T>. Entonces, lo que más me molesta es la idea de que, por alguna razón, las funciones son ambiguas cuando, por lo que sé, no lo son. El método addListener() está sobrecargado para diferentes tipos de entrada IListener<const SConsolePacket&> y IListener<const SControlPacket&>. El uso es:

m_controller->addListener(m_model); 

Dónde m_model es un puntero a un objeto IRigidBody, y IRigidBody hereda sólo de IListener< const SControlPacket& > y sin duda no de IListener< const SConsolePacket& >

Como una comprobación de validez, solía doxygen para generar el diagrama de jerarquía de clases y doxygen está de acuerdo conmigo en que IRigidBody no deriva de IListener< const SConsolePacket& >

Evidentemente, mi comprensión de la herencia en C++ no es exactamente correcta. Estoy bajo la impresión de que IListener<const SControlPacket&> y IListener<const SConsolePacket&> son dos tipos diferentes, y que las declaraciones de funciones

addListener(IListener<const SConsolePacket&>* listener) 

y

addListener(IListener<const SControlPacket&>* listener) 

declarar dos funciones separadas que hacen dos cosas distintas dependiendo de la (distinta) tipo diferente del parámetro que se ingresa Además, tengo la impresión de que un puntero a un IRigidBody también es un puntero a un IListener<const SControlPacket&> y que al llamar al addListener(m_model) el compilador debe entender que estoy llamando a la segunda de las dos funciones anteriores.

Incluso he intentado fundición m_model así:

m_controller->addListener(
     static_cast<IListener<const SControlPacket&>*>(m_model)); 

pero aún así obtener ese error. No puedo ver cómo estas funciones son ambiguas. ¿Alguien puede arrojar luz sobre este tema?

P.S. Yo sé cómo forzar la función de ser un-ambigua al hacer esto:

m_controller->ISource<const SControlPacket&>::addListener(m_model); 

Lo que pasa es que creo que es terriblemente unreadible y preferiría no tener que hacer eso.

Editar ... es una broma. Que al parecer no se soluciona el problema, ya que conduce a un error de vinculador:

CModelTesterGui.cpp:1312: undefined reference to `utility::ISource<aerobat::SControlPacket const&>::addListener(utility::IListener<SControlPacket const&>*)' 
+0

¿Cuál es la relación entre SControlPacket y SConsolePacket? – GRB

+0

¿Podría agregar por qué la última línea 'm_controller-> ISource :: addListener (m_model);' desambigua la llamada? Si las funciones están sobrecargadas, deberían estar en la misma clase. ¿Dónde se declaran esas funciones? –

+0

@GRB No hay relación. Ambas son estructuras derivadas de la nada. @litb Me equivoqué al respecto. Lo hizo compilar pero resultó en un error de enlace cuando el enlazador intenta encontrar la fuente <...> :: addListener (...) que es puramente virtual. Ahora estoy muy confundido. Cuando dices declarado, supongo que te refieres a definido. Se definen en las clases concerete que se derivan de IController. – cheshirekow

Respuesta

20

Parece que su situación es la siguiente:

struct A { 
    void f(); 
}; 

struct B { 
    void f(int); 
}; 

struct C : A, B { }; 

int main() { 
    C c; 
    c.B::f(1); // not ambiguous 
    c.f(1); // ambiguous 
} 

La segunda llamada a f es ambigua, porque en mirando hacia arriba el nombre, encuentra funciones en dos ámbitos de clase base diferentes. En esta situación, la búsqueda es ambigua: no se sobrecargan entre sí. Una solución sería usar una declaración de uso para cada nombre de miembro.Operaciones de búsqueda encontrará nombres en el ámbito de C y no más las operaciones de búsqueda:

struct C : A, B { using A::f; using B::f; }; 

Ahora, la llamada podría encontrar dos funciones, hacer la resolución de sobrecarga, y encontrar que el que toma int caben. Llevada a su código, eso significaría que usted tiene que hacer algo como lo siguiente

struct controller : ISource<const SConsolePacket&>, ISource<const SControlPacket&> { 
    using ISource<const SConsolePacket&>::addListener; 
    using ISource<const SControlPacket&>::addListener; 
}; 

Ahora, los dos nombres están en el mismo ámbito, y ahora pueden sobrecargar el uno al otro. La búsqueda ahora se detendrá en la clase de controlador, sin profundizar en las dos ramas de la clase base.

+0

AGRADABLE! Entonces para eso son esas cosas. No creo haber entendido realmente el uso de declaraciones (una consecuencia de no entender realmente la búsqueda de nombres). Muchas gracias. Como nota, de hecho puse la declaración using en la interfaz de IController. Creo que ese es el lugar apropiado para eso. – cheshirekow

+0

Al menos en g ++, dos usos no están permitidos. O elige A :: f o B :: f en la clase derivada C. – Dingle

+0

¡Gracias! Como siempre, una respuesta muy clara y concisa: D –

Cuestiones relacionadas