Ошибка с вложенным наследованием класса
class A {};
class B : private A {};
class C : private B
{
public:
class D : private A {}; // Error here
};
Этот код дает следующую ошибку (в VS 2013):
nested.cpp(8): ошибка C2247: "A" недоступна, потому что "B" использует 'private' для наследования от "A"
Он фиксируется, если я изменяю определение D
следующим образом:
class D : private ::A {};
Это правильное поведение, и если да, то почему?
Сначала я думал, что это потому, что C
наследует конфиденциально от B
, который скроет базовые классы. Но если я исключаю класс "средний человек" B
и просто использую это:
class A {};
class C : private A
{
public:
class D : private A {};
};
Ошибка исчезнет.
Ответы
Ответ 1
Цитата из cppreference:
Имя, которое является приватным в соответствии с неквалифицированным поиском имен, может быть доступный через квалифицированный поиск имени
С учетом этого давайте посмотрим, как безотказный поиск имени будет работать для первого примера:
class A {};
class B : private A {};
class C : private B
{
public:
class D : private A {}; // Error here
};
Опять же, согласно цитате, она должна работать, если вы используете полное имя, как показано на рисунке
class D : private ::A {};
И что касается вашего последнего примера:
class A {};
class C : private A
{
public:
class D : private A {};
};
Это работает, потому что поиск имени работает для всех имен, которые являются частью одного и того же класса. Чтобы снова указать cppreference:
Все члены класса (тела функций-членов, инициализаторы объекты-члены и все вложенные определения классов) имеют доступ ко всем именам, к которым может получить доступ класс.
Ответ 2
Это вопрос областей при поиске имени:
-
Когда вы используете ::A
это полное имя, поэтому вы явно ссылаетесь на глобальное пространство имен и выбираете A
оттуда.
-
Когда вы наследуете от A
, C
(скажем) видит A
, и вы можете напрямую ссылаться на имя A
в C
с неквалифицированным именем.
-
Когда вы наследуете от B
, C
видит B
и A
является закрытым в своей области. Это личное, но оно существует. Поскольку A
является неквалифицированным именем и ищет в первую очередь в этой области, он оказывается найденным и недоступным, поэтому ошибка.
Ответ 3
Пример из cppreference:
class A { };
class B : private A { };
class C : public B {
A* p; // error: unqualified name lookup finds A as the private base of B
::A* q; // OK, qualified name lookup finds the namespace-level declaration
};
С частным наследованием открытый и защищенный член базового класса становятся частными членами производного класса.
class B : private A {};
class C : private B
{
public:
class D : private A {}; // Error because all members of A is private to B so what
//would be the use of this private inheritance if you can't access any of A member.
};
В то время как
class D :private ::A {};
работает, потому что члены A непосредственно берутся из глобального пространства имен, позволяя D
иметь доступ к A
общедоступным и защищенным членам.
Ответ 4
Важнейшая часть понимания, которая явно не прописана в других ответах, такова:
Имя класса вводится в область видимости классов.
То есть, если у вас есть
class A {};
то вы можете обратиться к классу A
не только по имени ::A
, но и по имени A::A
. Обратите внимание, что, несмотря на описание одного и того же класса, это не одно и то же имя, поскольку они находятся в разных областях.
Теперь, когда в области A
или класса, получающего прямо или косвенно из A
, неквалифицированный поиск найдет A::A
, а не ::A
(если A::A
сам не скрыт еще одним именем).
Кроме того, в отличие от некоторых других языков, С++ не скрывает частные имена из областей, где вы не можете получить к ним доступ, но использует спецификаторы доступа только как разрешение на использование имени. Более того, эти разрешения привязаны к имени, а не к именованному объекту (в данном случае классу).
Итак, в вашем коде, при неквалифицированном поиске A
, компилятор находит имя C::B::A::A
, которое скрывает имя ::A
, а затем проверяет разрешение доступа и находит это имя закрытым в текущем контексте, так как оно это имя в области C::B::A
, к которому невозможно получить доступ из C
, поскольку A
является частным базовым классом B
.
Ответ 5
класс D: private:: A {};
Когда вы наследуете от B, C видит, что B и A являются частными в своей области. Это личное, но оно существует. Поскольку A является неквалифицированным именем и ищет в первую очередь в этой области, он оказывается найденным и недоступным, поэтому ошибка.