Mam dwie klasy podstawowe z wykorzystaniem klauzul
class MultiCmdQueueCallback {
using NetworkPacket = Networking::NetworkPacket;
....
}
class PlcMsgFactoryImplCallback {
using NetworkPacket = Networking::NetworkPacket;
....
}
Następnie deklaruję klasę
class PlcNetwork :
public RouterCallback,
public PlcMsgFactoryImplCallback,
public MultiCmdQueueCallback {
private:
void sendNetworkPacket(const NetworkPacket &pdu);
}
kompilator następnie oznacza odniesienie błędu do „NetworkPacket” jest niejednoznaczne „sendNetworkPacket (NetworkPacket i ...”
Teraz obie „klauzule używające” rozwiązują tę samą klasę bazową Networking: NetworkPacket
i w rzeczywistości, jeśli zastąpię deklarację metody:
void sendNetworkPacket(const Networking::NetworkPacket &pdu);
dobrze się kompiluje.
Dlaczego kompilator traktuje każdą klauzulę używającą jako odrębny typ, mimo że oba wskazują na ten sam typ bazowy. Czy jest to wymagane przez standard, czy mamy błąd kompilatora?
c++
using-declaration
Andrew Goedhart
źródło
źródło
NetworkPacket
- w MultiCmdQueueCallback, w PlcMsgFactoryImplCallback, w sieci. Który z nich powinien zostać użyty. I nie sądzę, żeby wprowadzenievirtual
mogło tu pomóc.Odpowiedzi:
Przed spojrzeniem na typ wynikowy aliasu (i dostępność)
patrzymy na imiona
i rzeczywiście
NetworkPacket
może byćMultiCmdQueueCallback::NetworkPacket
PlcMsgFactoryImplCallback::NetworkPacket
Fakt, na który oba wskazują,
Networking::NetworkPacket
jest nieistotny.Robimy rozpoznawanie imienia, co powoduje niejasności.
źródło
error: [...] is private within this context
.class A { public: void f(char, int) { } private: void f(int, char) { } }; void demo() { A a; a.f('a', 'd'); }
- nie to samo, ale rozwiązanie przeciążenia działa podobnie: Rozważ wszystkie dostępne funkcje, dopiero po wybraniu odpowiedniej rozważ rozważ dostępność ... W danym przypadku masz również dwuznaczność; jeśli zmienisz funkcję prywatną na akceptowanie dwóch znaków, zostanie ona wybrana, chociaż będzie prywatna - i wystąpi kolejny błąd kompilacji.Możesz po prostu rozwiązać dwuznaczność, ręcznie wybierając, którego chcesz użyć.
Kompilator szuka tylko definicji w klasach podstawowych. Jeśli ten sam typ i / lub alias jest obecny w obu klasach podstawowych, po prostu narzeka, że nie wie, którego użyć. Nie ma znaczenia, czy wynikowy typ jest taki sam, czy nie.
Kompilator szuka nazw tylko w pierwszym kroku, w pełni niezależny, jeśli jest to funkcja, typ, alias, metoda lub cokolwiek innego. Jeśli nazwy są niejednoznaczne, kompilator nie wykonuje żadnych dalszych działań! Po prostu narzeka na komunikat o błędzie i zatrzymuje się. Po prostu usuń niejednoznaczność z podaną instrukcją using.
źródło
Z dokumentów :
Chociaż te dwie
using
klauzule reprezentują ten sam typ, kompilator ma dwie możliwości w następującej sytuacji:Może wybierać między:
MultiCmdQueueCallback::NetworkPacket
iPlcMsgFactoryImplCallback::NetworkPacket
ponieważ dziedziczy z obu
MultiCmdQueueCallback
iPlcMsgFactoryImplCallback
bazowych klas. Rezultatem rozpoznawania nazw kompilatora jest błąd niejednoznaczności. Aby to naprawić, musisz wyraźnie poinstruować kompilator, aby używał jednego lub drugiego w ten sposób:lub
źródło
class C { void f(uint32_t); }; void C::f(unsigned int) { }
(pod warunkiem dopasowania aliasów). Skąd więc różnica? Nadal są tego samego typu, co potwierdzają twoje cytaty (których nie uważam za wystarczające do wyjaśnienia) ...class C : public A, public B { void f(A::D); }; void C::f(B::D) { }
- przynajmniej GCC akceptuje.Istnieją dwa błędy:
prywatny-prywatny
Nie widzę problemu, że kompilator narzeka najpierw na drugi problem, ponieważ kolejność nie ma tak naprawdę znaczenia - musisz rozwiązać oba problemy, aby kontynuować.
publiczny-publiczny
Jeśli zmienisz widoczność zarówno
MultiCmdQueueCallback::NetworkPacket
iPlcMsgFactoryImplCallback::NetworkPacket
na publicznym lub chronionym, potem drugi emisyjnej (dwuznaczności) jest oczywista - to są dwie różne aliasy typu choć mają ten sam podstawowy typ danych. Niektórzy mogą myśleć, że „sprytny” kompilator może rozwiązać ten problem (konkretny przypadek), ale należy pamiętać, że kompilator musi „myśleć ogólnie” i podejmować decyzje w oparciu o reguły globalne zamiast robić wyjątki dla poszczególnych przypadków. Wyobraź sobie następujący przypadek:Czy kompilator powinien traktować
NetworkPacketID
to samo? Na pewno nie. Ponieważ w systemie 32-bitowymsize_t
jest on 32-bitowy, auint64_t
zawsze jest 64-bitowy. Ale jeśli chcemy, aby kompilator sprawdzał podstawowe typy danych, nie mógł rozróżnić tych w systemie 64-bitowym.publiczny prywatny
Uważam, że ten przykład nie ma sensu w przypadku użycia OP, ale ponieważ tutaj rozwiązujemy problemy w ogóle, rozważmy:
Myślę, że w tym przypadku kompilator powinien traktować
PlcNetwork::NetworkPacket
tak,PlcMsgFactoryImplCallback::NetworkPacket
ponieważ nie ma innych wyborów. Dlaczego nadal tego nie robi i obwinianie się o dwuznaczność jest dla mnie tajemnicą.źródło