Частный базовый класс и множественное наследование - программирование
Подтвердить что ты не робот

Частный базовый класс и множественное наследование

Рассмотрим:

struct A { int x;};
struct B : A {};
struct C : private A {};

Теперь, как и ожидалось, код

struct D : C
{
    D () { C::x = 2; }
};

int main () { D d; }

не компилируется:

test2.cc: In constructor ‘D::D()’:
test2.cc:1:16: error: ‘int A::x’ is inaccessible
test2.cc:7:12: error: within this context

Теперь, если я делаю

struct D : B, C
{
    D () { C::x = 2; }
};

int main () { D d; }

тогда ошибка исчезнет! Разве A::x тоже не может быть недоступен? Какое объяснение здесь?

Я использую gcc version 4.7.2 (GCC), linux x86_64, если это имеет значение.

EDIT: Он не компилируется с Clang 3.2: clang 3.2

Но это происходит с gcc 4.7.2: gcc 4.7.2

4b9b3361

Ответ 1

Это, безусловно, ошибка . Нет причин, по которым наследование класса B также должно изменить доступность членов C.

Даже GCC 4.8.0 (бета), похоже, не решила эту проблему. Clang 3.2 и ICC 13.0.1, с другой стороны, правильно отказаться от компиляции этого кода.

Ответ 2

Ответ: clang является правильным. Тем не менее, код может также терпеть неудачу как неоднозначный в соответствии со стандартом.

Если вы посмотрите на 11.2p5, у него есть соответствующая заметка (да, я знаю, что ноты ненормативны):

[Примечание. Этот класс может быть явным, например, когда используется квалифицированный идентификатор, или неявным, например, когда используется оператор доступа класса (5.2.5) (включая случаи, когда добавляется неявное "this- > " ). Если оба класса оператор доступа к члену и квалифицированный идентификатор используются для обозначения члена (как в p->T::m), класс, назвавший элемент, - это класс, обозначаемый спецификатор вложенного имени идентификатора (т.е. T). -end note ]

Что означает эта заметка, так это то, что если вы добавите this-> в C::x = 2;, то C - это класс, называя член и gcc 4.7.2 правильно выполняется, если это так.

Теперь вопрос Кто является классом, назвавшим член для C::x? naming class определяется тем же 11.2p5:

Доступ к члену зависит от класса, в котором член по имени. Этот класс именования - это класс, в котором имя участника было посмотрел и нашел.

Теперь поиск имен для членов класса указан в 10.2, и после прочтения всего этого я пришел к выводу, что x представляет собой union для подобъектов в соответствии с:

В противном случае новый S (f, C) представляет собой набор поиска с общим набором декларации и объединение подобъектных множеств.

Это означает, что согласно правилам поиска членов x может быть либо от B, либо A! Это делает код плохо сформированным как: Name lookup can result in an ambiguity, in which case the program is ill-formed. Однако эта двусмысленность может быть разрешена согласно 10.2p8:

Неоднозначность может быть разрешена путем присвоения имени его классу имя.

И из источника Clang, мы видим, что это то, что они решили сделать:

// If the member was a qualified name and the qualified referred to a
// specific base subobject type, we'll cast to that intermediate type
// first and then to the object in which the member is declared. That allows
// one to resolve ambiguities in, e.g., a diamond-shaped hierarchy such as:
//
//   class Base { public: int x; };
//   class Derived1 : public Base { };
//   class Derived2 : public Base { };
//   class VeryDerived : public Derived1, public Derived2 { void f(); };
//   void VeryDerived::f() {
//     x = 17; // error: ambiguous base subobjects
//     Derived1::x = 17; // okay, pick the Base subobject of Derived1
//   }

Однако обратите внимание на can в формулировке приведенной выше цитаты: often can be resolved. Это означает, что они не обязательно должны быть разрешены. Таким образом, я думаю, что согласно стандарту код должен терпеть неудачу как неоднозначный или как отказ доступа к частному члену.

ИЗМЕНИТЬ

Существует некоторое утверждение о интерпретации can и о том, существует ли здесь двусмысленность. Я нашел Отчет о дефектах 39. Споры о противоречивости неопределенности говорят об этой проблеме.

Ответ 3

Я не уверен в конкретной причине, но я знаю это:

когда вы используете множественное наследование в виде алмаза, подобном этому, у вас будет несколько копий базового класса A в выведенном объекте D.

В вашем случае объект D имеет 2 члена с именем A:: x, которые могут вызвать путаницу из компилятора.

это называется Diamon Problem