Derived1 :: Base et Derived2 :: Base font-ils référence au même type?

12

MSVC, Clang et GCC sont en désaccord sur ce code:

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 donne une erreur similaire et MSVC ne donne aucune erreur.

Qui est ici?

Je suppose que cela est couvert dans [class.member.lookup] , mais j'ai du mal à comprendre ce qu'il essaie de me dire pour ce cas. Veuillez citer les parties pertinentes et si possible expliquer en anglais simple.

PS: Inspiré par cette question Pourquoi la référence à la classe de base est-elle ambiguë avec la classe dérivée :: -operator trough?

PPS: En fait, mon doute est de savoir si se Der1::Baseréfère au type, qui serait Base(et Der2::Baseest alors exactement le même type), ou au sous-objet. Je suis convaincu que c'est le premier, mais si c'est le dernier, alors MSVC aurait raison.

idclev 463035818
la source
@LanguageLawyer plus de bons indices que gcc et clang ont raison, mais je ne suis pas entièrement convaincu que mscv est faux
idclev 463035818
1
Évidemment, les deux se réfèrent au même type ::Base, mais la vraie question semble être légèrement différente ici. Il existe deux sous-objets de type Base, et les deux ont un Base::x membre.
MSalters le
@MSalters the PPS exprime simplement ma confusion. Si vous avez une suggestion pour un meilleur titre, n'hésitez pas à le modifier, je ne l'aime pas moi-même (car je sais que la réponse est oui), mais je n'ai pas trouvé quelque chose de plus approprié
idclev 463035818
1
@ d4rk4ng31 il n'y a pas d'héritage virtuel dans le code (exprès)
idclev 463035818

Réponses:

6

Pour répondre à la question dans le titre, oui, fait Derived1::Baseréférence au nom de classe injecté [class.pre] Base et il en est de même Derived2::Base. Les deux se réfèrent à la classe ::Base.

Maintenant, si Baseaurait un membre statiquex , la recherche de Base::xserait sans ambiguïté. Il n'y en a qu'un.

Le problème dans cet exemple est qu'il xs'agit d'un membre non statique et AllDera deux de ces membres. Vous pouvez lever l' ambiguïté d'x un tel accès en spécifiant une classe de base non ambiguëAllDer qui n'a qu'un seul xmembre. Derived1est une classe de base non ambiguë, et elle a un xmembre, Derived1::xspécifie donc sans ambiguïté lequel des deux xmembres AllDervous voulez dire. Basea également un seul xmembre, mais ce n'est pas une base univoque de AllDer. Chaque instance de AllDerpossède deux sous-objets de type Base. C'est donc Base::xambigu dans votre exemple. Et comme Derived1::Basec'est juste un autre nom pour Base, cela reste ambigu.

[class.member.lookup] spécifie qu'il xest recherché dans le contexte du spécificateur de nom imbriqué, donc cela doit être résolu en premier. Nous recherchons en effet Base::x, non Derived1::x, car nous avons commencé par résoudre en Derived1::Basetant que Base. Cette partie réussit, il n'y en a qu'un xdans la Base.note 12 dans [class.member.lookup] vous indique explicitement qu'une utilisation d'une recherche de nom sans ambiguïté peut toujours échouer lorsqu'il existe plusieurs sous-objets avec le même nom. D::idans cet exemple est fondamentalement votre Base::x.

MSalters
la source
donc seulement "peut échouer" mais pas "doit échouer" et les trois compilateurs ont raison? Considérons par exemple un template <typname A,typename B> struct foo : A,Bavec un code artificiel pour accéder aux membres d'une base de Aet B. Dans ce cas, je veux obtenir une erreur
idclev 463035818
1
@ idclev463035818: Je soupçonne que c'est un diagnostic "doit échouer" requis . En particulier, cela échoue sur l'accès des membres de la classe 7.6.1.4/7, "mal formé".
MSalters le
Je dois faire un peu plus de lecture. Et je dois faire des recherches sur msvc, je soupçonne que certains indicateurs sont nécessaires pour obtenir une sortie entièrement conforme, mais je dois trouver quels indicateurs
idclev 463035818
2

La raison pour laquelle vous pouvez faire référence au nom de la classe en tant que membre de la classe est que cpp l'alias pour une utilisation pratique, comme si vous écriviez using Base = ::Base;dans Base.
Le problème auquel vous êtes confronté Der1::Baseest le suivant Base.
Ainsi, lorsque vous écrivez Der1::Base::x, c'est la même chose que Base::x.

Dani
la source
Je sais quel est "le problème" mais vous ne répondez pas à ma question: "Qui a raison? (Et pourquoi?)"
idclev 463035818
@ idclev463035818: Je pense que c'est le bon côté. La partie sur usingest écrite dans la norme, et je pense que c'est la clé pour interpréter ce que dit l'expression.
Dani
@ idclev463035818: regardez l'exemple suivant. Je pense que cela éclaircira un peu les choses. ideone.com/IqpXjT
Dani
désolé mais je pense que vous ne comprenez pas le point de ma question. Je veux savoir ce qui est correct selon la norme, car je vois différents compilateurs faire des choses différentes. Regarder ce qu'un compilateur particulier fait à un exemple particulier n'aide pas à répondre à la question
idclev 463035818
Pour info, MSVC (ver 19.25.28614 pour x64) ne parvient pas à compiler votre exemple sur ideone.com/IqpXjT . En utilisant cl /c /Wall /WX /Od /MDd /Za /permissive- /std:c++17 main.cppcomme ligne de commande, j'obtiensmain.cpp(7): error C2597: illegal reference to non-static member 'A::x'
heap underrun