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