Czy Derived1 :: Base i Derived2 :: Base odnoszą się do tego samego typu?

12

MSVC, Clang i GCC nie zgadzają się na ten kod:

struct Base { int x; };
struct Der1 : public  Base {};
struct Der2 : public  Base {};

struct AllDer : public Der1, public Der2 {
    void foo() {
        Der1::Base::x = 5;
    }
};

Godbolt

GCC:

<source>: In member function 'void AllDer::foo()':    
<source>:10:21: error: 'Base' is an ambiguous base of 'AllDer'    
   10 |         Der1::Base::x = 5;    
      |                     ^    
Compiler returned: 1

Clang daje podobny błąd, a MSVC nie daje błędu.

Kto tu jest?

Przypuszczam, że jest to omówione w [class.member.lookup] , ale mam trudności ze zrozumieniem, co próbuje mi powiedzieć w tej sprawie. Proszę podać odpowiednie części i, jeśli to możliwe, wyjaśnić je prostym językiem angielskim.

PS: Zainspirowany tym pytaniem Dlaczego odniesienie do klasy podstawowej jest niejednoznaczne z klasą pochodną :: -operator?

PPS: Właściwie mam wątpliwości, czy Der1::Baseodnosi się do typu, który byłby Base(a następnie Der2::Basejest dokładnie tego samego typu), czy do podobiektu. Jestem przekonany, że jest to pierwszy, ale jeśli tak, to MSVC miałoby rację.

idclev 463035818
źródło
@LanguageLawyer więcej dobrych wskazówek, że gcc i clang mają rację, ale nie jestem w pełni przekonany, że mscv jest zły
idclev 463035818
1
Oczywiście oba odnoszą się do tego samego rodzaju ::Base, ale prawdziwe pytanie wydaje się tutaj nieco inne. Istnieją dwa podobiekty typu Basei oba mają element Base::x członkowski.
MSalters
@MSalters PPS wyraża jedynie moje zamieszanie. Jeśli masz sugestię dotyczącą lepszego tytułu, możesz go edytować, nie lubię tego (bo wiem, że odpowiedź brzmi tak), ale nie znalazłem czegoś bardziej odpowiedniego
idclev 463035818
1
@ d4rk4ng31 w kodzie nie ma wirtualnego dziedziczenia (celowo)
idclev 463035818

Odpowiedzi:

6

Aby odpowiedzieć na pytanie w tytule, tak, Derived1::Baseodwołuje się do nazwy wstrzykniętej klasy [class.pre] Base i tak też jest Derived2::Base. Oba odnoszą się do klasy ::Base.

Teraz, jeśli Basemiałoby statyczny element x, a następnie odnośnika od Base::xbyłaby jednoznaczna. Jest tylko jeden.

Problem w tym przykładzie polega na tym, że xjest to element niestatyczny, iAllDer ma dwa takie elementy. Możesz ujednoznacznić taki dostęp x, określając jednoznaczną klasę podstawową, AllDerktóra ma tylko jednego xczłonka. Derived1jest jednoznaczną klasą podstawową i ma jednego xczłonka, więc Derived1::xjednoznacznie określa, który z dwóch xczłonków w AllDertobie znaczy. Baseteż ma tylko jednego xczłonka, ale nie jest to jednoznaczna podstawa AllDer. Każde wystąpienie AllDerma dwa podobiekty typu Base. Dlatego Base::xjest niejednoznaczny w twoim przykładzie. A ponieważ Derived1::Basejest to tylko inna nazwa Base, pozostaje to niejednoznaczne.

[class.member.lookup] określa, że xjest sprawdzane w kontekście specyfikatora zagnieżdżonej nazwy, więc najpierw trzeba to rozwiązać. Rzeczywiście szukamy Base::x, nie Derived1::x, ponieważ zaczęliśmy od rozwiązania Derived1::Baseas Base. Ta część kończy się sukcesem, jest tylko jeden xw Base.uwadze 12 w [class.member.lookup], który wyraźnie mówi, że użycie jednoznacznego wyszukiwania nazwy może nadal zawieść, gdy istnieje wiele podobiektów o tej samej nazwie. D::iw tym przykładzie jest w zasadzie twój Base::x.

MSalters
źródło
więc tylko „może zawieść”, ale nie „musi zawieść” i wszystkie trzy kompilatory mają rację? Rozważ na przykład a template <typname A,typename B> struct foo : A,Bz jakimś wymyślonym kodem, aby uzyskać dostęp do członków bazy Ai B. W takim przypadku chcę otrzymać błąd
idclev 463035818
1
@ idclev463035818: Podejrzewam, że wymagana jest diagnostyka „musi zawieść” . W szczególności kończy się to niepowodzeniem w przypadku dostępu członków klasy 7.6.1.4/7 „źle sformułowany”.
MSalters
Muszę trochę więcej czytać. I muszę przeprowadzić badania dotyczące msvc, podejrzewam, że niektóre flagi są potrzebne, aby uzyskać w pełni zgodny wynik, ale muszę się dowiedzieć, które flagi
idclev 463035818
2

Powodem, dla którego możesz odwoływać się do nazwy klasy jako członka klasy, jest to, że cpp aliasuje ją dla wygodnego użycia, tak jakbyś pisał using Base = ::Base;w Base.
Problem polega na tym, że Der1::Basejest Base.
Tak więc, kiedy piszesz Der1::Base::x, jest to to samo co Base::x.

Dani
źródło
Wiem, na czym polega „problem”, ale nie odpowiadasz na moje pytanie: „Kto ma rację? (I dlaczego?)”
idclev 463035818
@ idclev463035818: Wierzę, że to jest właściwa strona. Część o usingjest napisana w standardzie i myślę, że to jest klucz do interpretacji tego, co mówi to wyrażenie.
Dani
@ idclev463035818: Spójrz na następujący przykład. Myślę, że to trochę wyjaśni. ideone.com/IqpXjT
Dani
przepraszam, ale myślę, że nie rozumiesz sensu mojego pytania. Chcę wiedzieć, co jest zgodne ze standardem, ponieważ widzę, że inny kompilator robi różne rzeczy. Spojrzenie na to, co robi jeden kompilator z jednym konkretnym przykładem, nie pomaga odpowiedzieć na pytanie
idclev 463035818
Po prostu FYI, MSVC (wersja 19.25.28614 dla x64) nie kompiluje twojego przykładu na ideone.com/IqpXjT . Używając cl /c /Wall /WX /Od /MDd /Za /permissive- /std:c++17 main.cppjako wiersza poleceń, dostajęmain.cpp(7): error C2597: illegal reference to non-static member 'A::x'
kupę zabrakło