2009-08-21 30 views
9

sto ottenendo il seguente errore di compilazione in una delle mie classi, usando gcc 3.4.5 (mingw):richiesta di membro `...' è ambiguo in 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&] 

Speriamo che si può vedere che ISource<T> è un'interfaccia modello che indica solo che l'oggetto può essere un informatore per un oggetto di tipo corrispondente IListener<T>. Quindi la cosa che mi ha infastidito è questa idea che per qualche motivo le funzioni sono ambigue quando, per quanto posso dire, non lo sono. Il metodo addListener() è sovraccarico per diversi tipi di input IListener<const SConsolePacket&> e IListener<const SControlPacket&>. L'utilizzo è:

m_controller->addListener(m_model); 

Dove m_model è un puntatore a un oggetto IRigidBody e IRigidBody eredita solo IListener< const SControlPacket& > e sicuramente non da IListener< const SConsolePacket& >

Come un controllo di integrità, ho usato doxygen per generare lo schema gerarchico classe e doxygen concorda con me sul fatto che IRigidBody non deriva da IListener< const SConsolePacket& >

Evidentemente la mia comprensione dell'ereditarietà in C++ non è esattamente corretta. Sono sotto l'impressione che IListener<const SControlPacket&> e IListener<const SConsolePacket&> sono due tipi diversi, e che le dichiarazioni di funzione

addListener(IListener<const SConsolePacket&>* listener) 

e

addListener(IListener<const SControlPacket&>* listener) 

dichiaro due funzioni separate che fanno due cose separate a seconda della (distinti) diverso tipo di parametro che viene immesso. Inoltre, ho l'impressione che un puntatore a un IRigidBody sia anche un puntatore a un IListener<const SControlPacket&> e che chiamando il addListener(m_model) il compilatore dovrebbe capire che sto chiamando la seconda delle due funzioni di cui sopra.

Ho anche cercato di lanciare m_model come questo:

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

ma ancora ottenere quell'errore. Non posso per la vita di me vedere come queste funzioni sono ambigue. Qualcuno può far luce su questo problema?

P.S. So come forzare la funzione di essere non-ambigua in questo modo:

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

Ho appena capita di pensare che è terribilmente unreadible e io preferirei non avere a che fare.

Modifica ... sto scherzando. Che a quanto pare non si risolve il problema in quanto porta a un errore di linker:

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

Qual è la relazione tra SControlPacket e SConsolePacket? – GRB

+0

Puoi aggiungere perché l'ultima riga 'm_controller-> ISource :: addListener (m_model);' disambigua la chiamata? Se le funzioni sono sovraccariche, dovrebbero essere nella stessa classe. Dove vengono dichiarate queste funzioni? –

+0

@GRB Nessuna relazione. Entrambe sono le strutture che derivano dal nulla. @litb Ho sbagliato su questo. Lo ha reso compilativo ma risulta essere un errore di collegamento quando il linker cerca di trovare ISource <...> :: addListener (...) che è puro virtuale. Ora sono molto confuso. Quando dici dichiarato dichiaro che intendi definito. Sono definiti nelle classi concerete che derivano da IController. – cheshirekow

risposta

20

Sembra che la vostra situazione è simile a questo:

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 seconda chiamata a f è ambigua, perché nel cercare i il nome, trova le funzioni in due diversi ambiti di base. In questa situazione, la ricerca è ambigua: non si sovraccaricano a vicenda. Una soluzione sarebbe utilizzare una dichiarazione usando per ogni nome di membro.Lookup troverà nomi nel campo di applicazione C e non occhiata oltre:

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

Ora, la chiamata potrebbe trovare due funzioni, fare la risoluzione di sovraccarico, e scoprire che quello in corso int si adatta. Riportati al vostro codice, vorrebbe dire che si deve fare qualcosa di simile al seguente

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

Ora, i due nomi sono nello stesso ambito, e ora possono sovraccaricare l'un l'altro. La ricerca ora si fermerà alla classe controller, senza immergersi ulteriormente nei due rami della classe base.

+0

NICE! Ecco a cosa servono queste cose. Non penso di aver mai veramente capito usando le dichiarazioni (una conseguenza di non capire veramente la ricerca del nome). Grazie mille. Come nota, inserisco effettivamente la dichiarazione using nell'interfaccia IController. Penso che sia il posto giusto per questo. – cheshirekow

+0

Almeno in g ++, due usi non sono consentiti. Puoi scegliere A :: f o B :: f nella classe derivata C. – Dingle

+0

Grazie! Come sempre una risposta davvero chiara e concisa: D –

Problemi correlati