Derived1 :: Base и Derived2 :: Base относятся к одному и тому же типу?

12

MSVC, Clang и GCC не согласны с этим кодом:

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 выдает похожую ошибку, а MSVC не выдает ошибки.

Кто здесь?

Я полагаю, что это описано в [class.member.lookup] , но мне трудно понять, что он пытается сказать мне для этого случая. Пожалуйста, укажите соответствующие части и, если возможно, объясните простым английским языком.

PS: Вдохновлен этим вопросом. Почему ссылка на базовый класс неоднозначна с классом :: -operator через производный класс?

PPS: На самом деле я сомневаюсь, Der1::Baseотносится ли это к типу, который будет Base(а затем Der2::Baseточно такого же типа), или к подобъекту. Я убежден, что это первое, но если оно будет последним, то MSVC будет прав.

idclev 463035818
источник
@LanguageLawyer дает больше хороших намеков на то, что gcc и clang правы, но я не до конца убежден, что mscv не прав
idclev 463035818
1
Очевидно, что оба относятся к одному и тому же типу ::Base, но реальный вопрос здесь немного отличается. Есть два подобъекта типа Base, и оба имеют Base::x члена.
MSalters
@MSalters PPS просто выражает мое замешательство. Если у вас есть предложение по улучшению заголовка, не стесняйтесь редактировать, мне самому это не нравится (потому что я знаю, что ответ - да), но я не нашел что-то более подходящее
idclev 463035818
1
@ d4rk4ng31 в коде нет виртуального наследования (специально)
idclev 463035818

Ответы:

6

Чтобы ответить на вопрос в заголовке, да, Derived1::Baseссылается на имя введенного класса [class.pre] Base и так же Derived2::Base. Оба относятся к классу ::Base.

Теперь, если Baseбы был статический член x, поиск Base::xбыл бы однозначным. Здесь только один.

Проблема в этом примере заключается в том, что xэто нестатический член, AllDerимеющий два таких члена. Вы можете устранить неоднозначность такого доступа x, указав однозначный базовый класс, в AllDerкоторый входит только один xчлен. Derived1является однозначным базовым классом, и он имеет один xчлен, поэтому Derived1::xоднозначно указывает, какой из двух xчленов в AllDerвас имеется в виду. Baseтоже имеет только одного xчлена, но это не однозначная основа AllDer. Каждый экземпляр AllDerимеет два подобъекта типа Base. Поэтому Base::xдвусмысленно в вашем примере. И поскольку Derived1::Baseэто просто другое название Base, это остается неоднозначным.

[class.member.lookup] указывает, что поиск производится xв контексте спецификатора вложенного имени, поэтому его необходимо решить в первую очередь. Мы действительно ищем Base::x, а не Derived1::xпотому, что мы начали с решения Derived1::Baseкак Base. Эта часть выполнена успешно, xв Base.примечании 12 в [class.member.lookup] содержится только одно сообщение о том, что использование однозначного поиска имени может все же завершиться неудачей, если существует несколько подобъектов с тем же именем. D::iв этом примере в основном ваш Base::x.

MSalters
источник
так что это только «может дать сбой», но не «должно произойти сбой», и все три компилятора верны? Рассмотрим, например, a template <typname A,typename B> struct foo : A,Bс некоторым надуманным кодом для доступа к членам базы Aи B. В таком случае я хочу получить ошибку
idclev 463035818
1
@ idclev463035818: Я подозреваю, что требуется диагностика "должен произойти сбой" . В частности, это терпит неудачу на 7.6.1.4/7 доступ члена класса, "плохо сформированный".
MSalters
Я должен сделать немного больше чтения. И я должен провести некоторое исследование на msvc, я подозреваю, что некоторые флаги необходимы для получения полностью совместимых выходных данных, но нужно выяснить, какие флаги
idclev 463035818
2

Причина, по которой вы можете ссылаться на имя класса как члена класса, заключается в том, что cpp псевдоним его для удобного использования, как если бы вы написали using Base = ::Base;внутри Base.
Проблема вы столкнулись в том , что Der1::Baseесть Base.
Таким образом, когда вы пишете Der1::Base::x, это так же, как Base::x.

Дани
источник
Я знаю, в чем «проблема», но вы не отвечаете на мой вопрос: «Кто прав? (И почему?)»
idclev 463035818
@ idclev463035818: Я считаю, что это правильная сторона. Часть о usingнаписана в стандарте, и я думаю, что это ключ к интерпретации того, что говорит выражение.
Дани
@ idclev463035818: Посмотрите на следующий пример. Я думаю, это немного прояснит ситуацию. ideone.com/IqpXjT
Дани
извините, но я думаю, вы не поняли суть моего вопроса. Я хочу знать, что правильно в соответствии со стандартом, потому что я вижу, что разные компиляторы делают разные вещи. Глядя на то, что один конкретный компилятор делает с одним конкретным примером, не помогает ответить на вопрос
idclev 463035818
Просто FYI, MSVC (версия 19.25.28614 для x64) не может скомпилировать ваш пример на ideone.com/IqpXjT . Используя в cl /c /Wall /WX /Od /MDd /Za /permissive- /std:c++17 main.cppкачестве командной строки, я получаюmain.cpp(7): error C2597: illegal reference to non-static member 'A::x'
куча опустошена