Beziehen sich Derived1 :: Base und Derived2 :: Base auf denselben Typ?

12

MSVC, Clang und GCC sind sich in diesem Code nicht einig:

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 gibt einen ähnlichen Fehler aus und MSVC gibt keinen Fehler aus.

Wer ist hier richtig?

Ich nehme an, dass dies in [class.member.lookup] behandelt wird , aber ich habe Schwierigkeiten zu verstehen, was es mir für diesen Fall zu sagen versucht. Bitte zitieren Sie die relevanten Teile und erklären Sie diese nach Möglichkeit in einfachem Englisch.

PS: Inspiriert von dieser Frage Warum ist der Verweis auf die Basisklasse mit der von :: -operator durch abgeleiteten Klasse nicht eindeutig?

PPS: Eigentlich bezweifle ich, ob es Der1::Basesich um den Typ handelt, der wäre Base(und dann Der2::Basegenau der gleiche Typ ist), oder um das Unterobjekt. Ich bin überzeugt, dass es das erste ist, aber wenn es das letztere ist, dann wäre MSVC richtig.

idclev 463035818
quelle
@LanguageLawyer mehr gute Hinweise, dass gcc und clang richtig sind, aber ich bin nicht ganz davon überzeugt, dass mscv falsch ist
idclev 463035818
1
Natürlich beziehen sich beide auf denselben Typ ::Base, aber die eigentliche Frage scheint hier etwas anders zu sein. Es gibt zwei Unterobjekte vom Typ Baseund beide haben ein Base::x Mitglied.
MSalters
@MSalters das PPS drückt nur meine Verwirrung aus. Wenn Sie einen Vorschlag für einen besseren Titel haben, können Sie ihn gerne bearbeiten. Ich mag ihn selbst nicht (weil ich weiß, dass die Antwort ja ist), habe aber nichts
Passenderes gefunden
1
@ d4rk4ng31 Es gibt keine virtuelle Vererbung im Code (absichtlich)
idclev 463035818

Antworten:

6

Um die Frage im Titel zu beantworten, Derived1::Baseverweist yes auf den Namen der injizierten Klasse [class.pre]Base und dies auch Derived2::Base. Beide beziehen sich auf die Klasse ::Base.

Wenn Basenun ein statisches Mitglied vorhanden wäre, wäre xdie Suche Base::xnach eindeutig. Es gibt nur einen.

Das Problem in diesem Beispiel ist , dass xein nicht-statisches Element und AllDerhat zwei solche Mitglieder. Sie können einen solchen Zugriff eindeutig definieren,x indem Sie eine eindeutige Basisklasse angeben, AllDerdie nur ein xMitglied hat. Derived1ist eine eindeutige Basisklasse und hat ein xMitglied. Derived1::xGibt also eindeutig an, welches der beiden xMitglieder in AllDerIhnen gemeint ist. BaseAuch hat nur ein xMitglied, aber es ist keine eindeutige Basis von AllDer. Jede Instanz von AllDerhat zwei Unterobjekte vom Typ Base. Daher Base::xist in Ihrem Beispiel nicht eindeutig. Und da dies Derived1::Basenur ein anderer Name ist Base, bleibt dies mehrdeutig.

[class.member.lookup] gibt an, dass xim Kontext des verschachtelten Namensbezeichners nachgeschlagen wird, sodass dieser zuerst aufgelöst werden muss. Wir suchen in der Tat Base::xnicht Derived1::x, weil wir damit begonnen haben, Derived1::Baseals zu lösen Base. Dieser Teil ist erfolgreich. xIn Base.Anmerkung 12 in [class.member.lookup] wird nur einer explizit darauf hingewiesen, dass die Verwendung einer eindeutigen Namenssuche möglicherweise immer noch fehlschlägt, wenn mehrere Unterobjekte mit demselben Namen vorhanden sind. D::iIn diesem Beispiel ist im Grunde Ihre Base::x.

MSalters
quelle
es kann also nur "fehlschlagen", aber nicht "muss fehlschlagen" und alle drei Compiler sind richtig? Betrachten Sie zum Beispiel a template <typname A,typename B> struct foo : A,Bmit einem erfundenen Code, um auf Mitglieder einer Basis von Aund zuzugreifen B. In diesem Fall möchte ich einen Fehler erhalten
idclev 463035818
1
@ idclev463035818: Ich vermute, es ist eine Diagnose erforderlich, die fehlschlagen muss . Dies schlägt insbesondere beim Zugriff auf 7.6.1.4/7 Klassenmitglieder fehl, "schlecht geformt".
MSalters
Ich muss ein bisschen mehr lesen. Und ich muss einige Nachforschungen über msvc anstellen. Ich vermute, dass einige Flags benötigt werden, um eine vollständig kompatible Ausgabe zu erhalten, aber ich muss herausfinden, welche Flags
idclev 463035818
2

Der Grund, warum Sie den Klassennamen als Mitglied der Klasse bezeichnen können, liegt darin, dass cpp ihn für eine bequeme Verwendung aliasisiert, als ob Sie using Base = ::Base;in Base geschrieben hätten.
Das Problem, mit dem Sie konfrontiert sind, Der1::Baseist das Base.
Wenn Sie also schreiben Der1::Base::x, ist es dasselbe wie Base::x.

Dani
quelle
Ich weiß, was "das Problem" ist, aber Sie beantworten meine Frage nicht: "Wer hat Recht? (Und warum?)"
idclev 463035818
@ idclev463035818: Ich glaube, das ist die richtige Seite. Der Teil über usingist im Standard geschrieben, und ich denke, das ist der Schlüssel zur Interpretation dessen, was der Ausdruck sagt.
Dani
@ idclev463035818: Sehen Sie sich das folgende Beispiel an. Ich denke, es wird ein bisschen aufklären. ideone.com/IqpXjT
Dani
Entschuldigung, aber ich denke, Sie verstehen den Punkt meiner Frage nicht. Ich möchte wissen, was gemäß dem Standard richtig ist, weil ich sehe, dass verschiedene Compiler verschiedene Dinge tun. Ein Blick darauf, was ein bestimmter Compiler mit einem bestimmten Beispiel macht, hilft nicht, die Frage zu beantworten
idclev 463035818
Nur zu Ihrer Information, MSVC (Version 19.25.28614 für x64) kann Ihr Beispiel unter ideone.com/IqpXjT nicht kompilieren . Unter Verwendung cl /c /Wall /WX /Od /MDd /Za /permissive- /std:c++17 main.cppals Befehlszeile ichmain.cpp(7): error C2597: illegal reference to non-static member 'A::x'
Heap - Underrun