2009-08-21 15 views
9

Dostaję następujący błąd kompilacji w jednej z moich klas, używając gcc 3.4.5 (MinGW):żądanie członka `...” jest niejednoznaczny w 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&] 

Mam nadzieję, że widać, że ISource<T> to interfejs szablonu, który po prostu wskazuje, że obiekt może być informatorem dla obiektu o pasującym typie: IListener<T>. Więc to, co mnie irytuje, polega na tym, że z jakiegoś powodu funkcje są niejednoznaczne, o ile mogę to stwierdzić, nie są. Metoda addListener() jest przeciążona dla różnych typów wejść IListener<const SConsolePacket&> i IListener<const SControlPacket&>. Użycie jest:

m_controller->addListener(m_model); 

Gdzie m_model jest wskaźnikiem do obiektu IRigidBody i IRigidBody dziedziczy tylko z IListener< const SControlPacket& > i na pewno nie z IListener< const SConsolePacket& >

Jako kontrola poprawności, użyłem doxygen wygenerować diagram hierarchii klas i doxygen zgadza się ze mną, że IRigidBody nie wywodzi się z IListener< const SConsolePacket& >

Oczywiście moje rozumienie dziedziczenia w C++ nie jest dokładnie poprawne. Jestem pod wrażeniem, że IListener<const SControlPacket&> i IListener<const SConsolePacket&> są dwa różne typy i że deklaracje funkcji

addListener(IListener<const SConsolePacket&>* listener) 

i

addListener(IListener<const SControlPacket&>* listener) 

zadeklarować dwie odrębne funkcje, które wykonują dwie odrębne rzeczy w zależności od (odrębny) inny typ wprowadzanego parametru. Co więcej, jestem pod wrażeniem, że wskaźnik do IRigidBody jest również wskaźnikiem do IListener<const SControlPacket&> i że wywołując addListener(m_model) kompilator powinien zrozumieć, że wywołuję drugą z powyższych dwóch funkcji.

Próbowałem nawet odlewania m_model tak:

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

ale wciąż dostać ten błąd. Nie mogę dla życia mnie zobaczyć, jak te funkcje są niejednoznaczne. Czy ktoś może rzucić światło na ten problem?

P.S. Wiem, jak zmusić funkcję być un-niejednoznaczne w ten sposób:

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

ja akurat uważam, że jest strasznie unreadible i wolałbym nie musieć tego robić.

Edytuj ... tylko żartuję. Że widocznie nie rozwiąże problemu, ponieważ prowadzi do błędu łącznikowej:

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

Jaki jest związek między SControlPacket a SConsolePacket? – GRB

+0

Czy możesz dodać, dlaczego ostatni wiersz 'm_controller-> ISource :: addListener (m_model);" disambiguates the call? Jeśli funkcje są przeciążone, powinny należeć do tej samej klasy. Gdzie są zadeklarowane te funkcje? –

+0

@GRB Brak związku. Oba są strukturami wywodzącymi się z niczego. @litb Myliłem się co do tego. Zmusiło go do kompilacji, ale okazało się, że prowadziło to do błędu łącza, gdy linker jedzie, aby znaleźć ISource <...> :: addListener (...), który jest czysty wirtualny. Teraz jestem bardzo zdezorientowany. Kiedy mówisz oświadczyłeś, że zakładam, że masz na myśli zdefiniowane. Są one zdefiniowane w klasach wydzielonych, które pochodzą od IController. – cheshirekow

Odpowiedz

20

wygląda sytuacji jest tak:

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 
} 

Drugie zaproszenie do f jest niejednoznaczny, ponieważ patrząc w górę nazwę, znajduje funkcje w dwóch różnych zakresach bazowych. W tej sytuacji wyszukiwanie jest niejednoznaczne - nie przeciążają się nawzajem. Rozwiązaniem byłoby użycie deklaracji użycia dla każdej nazwy członka.Lookup znajdzie nazw w zakresie C i nie odnośnika dalej:

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

Teraz wywołanie znajdzie dwie funkcje, czy rozdzielczość przeciążenie, a okaże się, że jeden biorąc int zmieści. Przeniesione do kodu, oznaczałoby to, że trzeba coś zrobić, jak na poniższym

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

Teraz obie nazwy są w tym samym zakresie, a teraz mogą przeciążać siebie. Lookup zatrzyma się teraz na klasie kontrolera, nie nurkując dalej w dwie gałęzie bazowe.

+0

ŁADNE! Więc do czego służą te rzeczy. Nie sądzę, abym kiedykolwiek zrozumiał używanie deklaracji (konsekwencja niezbyt zrozumiałego wyszukiwania nazw). Dziękuję Ci bardzo. Na marginesie, umieszczam deklarację użycia w interfejsie IController. Myślę, że to odpowiednie miejsce na to. – cheshirekow

+0

Przynajmniej w g ++ dwie wersje są niedozwolone. Możesz wybrać A :: f lub B :: f w klasie pochodnej C. – Dingle

+0

Dzięki! Jak zawsze bardzo wyraźna i zwięzła odpowiedź: D –

Powiązane problemy