Чи походять 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;
    }
};

Годболт

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

Кланг дає аналогічну помилку, а MSVC не дає помилок.

Хто тут правий?

Я вважаю, що це висвітлено в [class.member.lookup] , але у мене виникають труднощі з розумінням того, що він намагається сказати мені для цього випадку. Будь ласка, цитуйте відповідні частини та, якщо можливо, поясніть простою англійською мовою.

PS: Натхненний цим питанням Чому посилання на базовий клас неоднозначне з :: -оператором через похідний клас?

PPS: Насправді я сумніваюся, чи Der1::Baseвідноситься до типу, який був би Base(і тоді Der2::Baseточно такий же тип), або до субекти. Я переконаний, що це перше, але якщо це останнє, то MSVC було б правильним.



@LanguageLawyer більше добрих натяків на те, що gcc і clang мають рацію, але я не повністю переконаний, що mscv помиляється
idclev 463035818

1
Очевидно, що обидва посилаються на один і той же тип ::Base, але справжнє питання тут здається дещо іншим. Існує два суб’єкти типу Base, і обидва мають Base::x члена.
MSalters

@MSalters PPS просто виражає мою плутанину. Якщо у вас є пропозиція щодо кращого заголовка, не соромтесь редагувати, мені це не подобається (бо я знаю, що відповідь "так"), але не знайшов щось більш відповідне
idclev 463035818

1
@ d4rk4ng31 у коді немає віртуального успадкування
спеціально

Відповіді:


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.


тож лише "може провалитись", але не "повинен вийти з ладу", і всі три компілятори праві? Розглянемо, наприклад, 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'
купа недоїдання
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.