Страницы

Поиск по вопросам

пятница, 19 октября 2018 г.

Множественное наследование и VC++

В ходе дискуссии пришли к такой программе:
#include using namespace std;
class A { protected: int var; public: A(int x) { var = x; // Это обращение к A::var } };
class B: public A { protected: int var; public: B():A(2) { var = 4; // Обращение к B::var } };
class C: public A { protected: int var; public: C():A(3) { var = 6; // Обращение к C::var } };
class D: public B, public C { protected: int var; public:
void method() { var = B::A::var; // Должен выдать 2 cout << var << endl;
var = C::A::var; // Должен выдать 3 cout << var << endl;
var = B::var; // Должен выдать 4 cout << var << endl;
var = C::var; // Должен выдать 6 cout << var << endl; } };
int main() { D obj; obj.method(); }
Программа отлично компилируется и выводит то, что и ожидалось - в Visual C++ 2015. Попытка скомпилировать с помощью GCC на ideone.com дает массу ошибок:
prog.cpp: In member function 'void D::method()': prog.cpp:46:21: error: 'A' is an ambiguous base of 'D' var = B::A::var; // Должен выдать 2 ^ prog.cpp:49:21: error: 'A' is an ambiguous base of 'D' var = C::A::var; // Должен выдать 3 ^
Вопрос к знатокам стандарта - кто тут неправ, а кто прав? Если неправ VC++, то в чем, почему и как надо поступать правильно?
Update 22.10.2016
Пожалуй, наиболее переносимо будет не полагаться на name lookup, а воспользоваться преобразованиями в духе
var = static_cast(static_cast(this))->var; var = ((A*)(C*)this)->var;
Но при этом нужно делать var в A public'ом. (Опять же не понимаю, почему и где на это ссылка в стандарте...) Полный код тут - http://ideone.com/wt9yrz


Ответ

Как правильно указал @Ant, студия здесь неправа и всё дело в том, что подобные обращения A::B::C::D::E::F являются именно обращениями к вложенным(nested) сущностям, т.е. B должен быть сущностью вложенной в A, С в B и так далее. Это можно видеть в не нормативной ссылке в стандарте:
[basic.lookup.qual]p2 [Note: Multiply qualified names, such as N1::N2::N3::n, can be used to refer to members of nested classes (9.7) or members of nested namespaces. — end note]
Т.е. мы можем ссылаться на несколько уровней, но эти ссылки должны идти по нисходящей, никаких восходящих, либо же смешанных уровней.
Тогда почему это вообще компилируется?(оно компилируется пока не встречает неоднозначность, уберём неоднозначность и всё заработает). Потому что есть следующее правило по поиску скрытых имён:
[class.qual]p3 A class member name hidden by a name in a nested declarative region or by the name of a derived class member can still be found if qualified by the name of its class followed by the :: operator.
И тут получается, что C::A::var должно интерпретироваться как A::var, где C:: является избыточным квалификатором, который не несёт никакой смысловой нагрузки. Никакой другой интерпретации тут быть не может, т.к. C не содержит внутреннего класса с именем A.
Суть ошибки, кстати, хорошо видна с Resharper: он сразу показывается, что квалификаторы C:: и B:: избыточны.

Что касается второй части вопроса(обновления): не работает это по простой причине: классы наследники имеют доступ к данным предка только через this, у них нет доступа к данным произвольных объектов этих классов. Это описано в [class.access.base]p5, а конкретно, случай из вопроса, в (5.3):
m as a member of N is protected, and R occurs in a member or friend of class N, or in a member or friend of a class P derived from N, where m as a member of P is public, private, or protected

Комментариев нет:

Отправить комментарий