Derived1 :: Base e Derived2 :: Base si riferiscono allo stesso tipo?


12

MSVC, Clang e GCC non sono d'accordo su questo codice:

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 dà un errore simile e MSVC non dà alcun errore.

Chi è proprio qui?

Suppongo che questo sia trattato in [class.member.lookup] , ma ho difficoltà a capire cosa sta cercando di dirmi per questo caso. Si prega di citare le parti pertinenti e, se possibile, spiegare in un inglese semplice.

PS: ispirato a questa domanda Perché il riferimento alla classe base è ambiguo con la classe derivata :: -operator trough?

PPS: In realtà il mio dubbio è se si Der1::Baseriferisce al tipo, che sarebbe Base(e quindi Der2::Baseè esattamente lo stesso tipo) o all'oggetto secondario. Sono convinto che sia il primo, ma se è il secondo allora MSVC avrebbe ragione.



@LanguageLawyer altri buoni suggerimenti sul fatto che gcc e clang hanno ragione, ma non sono del tutto convinto che mscv sia sbagliato
idclev 463035818

1
Ovviamente entrambi si riferiscono allo stesso tipo ::Base, ma la vera domanda sembra essere leggermente diversa qui. Esistono due oggetti secondari di tipo Baseed entrambi hanno un Base::x membro.
MSalters il

@MSalters il PPS esprime semplicemente la mia confusione. Se hai un suggerimento per un titolo migliore, sentiti libero di modificarlo, non mi piace da solo (perché so che la risposta è sì), ma non ho trovato qualcosa di più appropriato
idclev 463035818

1
@ d4rk4ng31 non c'è eredità virtuale nel codice (apposta)
idclev 463035818

Risposte:


6

Per rispondere alla domanda nel titolo, sì, fa Derived1::Baseriferimento al nome della classe iniettata [class.pre] Base e così fa Derived2::Base. Entrambi si riferiscono alla classe ::Base.

Ora, se Baseavesse un membro staticox , la ricerca di Base::xsarebbe inequivocabile. Ce n'è solo uno.

Il problema in questo esempio è che xè un membro non statico e AllDerha due di questi membri. È possibile disambiguare tale accesso xspecificando una classe base non ambigua di AllDercui ha un solo xmembro. Derived1è una classe base non ambigua e ha un xmembro, quindi Derived1::xspecifica in modo inequivocabile quale dei due xmembri in AllDerte intendi. Baseanche ha un solo xmembro, ma non è una base inequivocabile di AllDer. Ogni istanza di AllDerha due oggetti secondari di tipo Base. Pertanto Base::xè ambiguo nel tuo esempio. E poiché Derived1::Baseè solo un altro nome per Base, questo rimane ambiguo.

[class.member.lookup] specifica che xviene cercato nel contesto dello specificatore di nomi nidificati, quindi deve essere risolto per primo. Certo Base::x, non lo Derived1::xstiamo cercando , perché abbiamo iniziato risolvendo Derived1::Basecome Base. Questa parte ha esito positivo, ce n'è solo uno xnella Base.Nota 12 in [class.member.lookup] che ti dice esplicitamente che l'uso di una ricerca di nomi non ambigua può ancora fallire quando ci sono più oggetti secondari con lo stesso nome. D::iin questo esempio è fondamentalmente tuo Base::x.


quindi solo "può fallire" ma non "deve fallire" e tutti e tre i compilatori hanno ragione? Si consideri ad esempio un template <typname A,typename B> struct foo : A,Bcon un codice elaborato per accedere ai membri di una base di Ae B. In tal caso, voglio ottenere un errore
idclev 463035818,

1
@ idclev463035818: Sospetto che sia richiesta una diagnostica "must fail" . In particolare, ciò non riesce sull'accesso dei membri della classe 7.6.1.4/7, "mal formato".
MSalters il

Devo leggere un po 'di più. E devo fare qualche ricerca su msvc, sospetto che siano necessari alcuni flag per ottenere un output pienamente conforme, ma devo scoprire quali flag
idclev 463035818

2

Il motivo per cui è possibile fare riferimento al nome della classe come membro della classe è perché cpp lo alias per un uso conveniente, come se si scrivesse using Base = ::Base;all'interno di Base.
Il problema che stai affrontando è che Der1::Baseè Base.
Quindi, quando scrivi Der1::Base::x, è lo stesso di Base::x.


So qual è "il problema" ma non stai rispondendo alla mia domanda: "Chi ha ragione? (E perché?)"
idclev 463035818

@ idclev463035818: credo che questa sia la parte giusta. La parte su usingè scritta nello standard e penso che sia la chiave per interpretare ciò che dice l'espressione.
Dani,

@ idclev463035818: guarda il seguente esempio. Penso che lo chiarirà un po '. ideone.com/IqpXjT
Dani

scusa ma penso che non capisca il punto della mia domanda. Voglio sapere cosa è giusto secondo lo standard, perché vedo diversi compilatori fare cose diverse. Guardare cosa fa un particolare compilatore a un esempio particolare, non aiuta a rispondere alla domanda
idclev 463035818

Cordiali saluti, MSVC (versione 19.25.28614 per x64) non riesce a compilare il tuo esempio su ideone.com/IqpXjT . Usando cl /c /Wall /WX /Od /MDd /Za /permissive- /std:c++17 main.cppcome riga di comando, ottengomain.cpp(7): error C2597: illegal reference to non-static member 'A::x'
heap underrun
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.