Частный базовый класс и множественное наследование
Рассмотрим:
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
Ответы
Ответ 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