Подтвердить что ты не робот

Ошибка с вложенным наследованием класса

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 {};
};

Ошибка исчезнет.

4b9b3361

Ответ 1

Цитата из cppreference:

Имя, которое является приватным в соответствии с неквалифицированным поиском имен, может быть доступный через квалифицированный поиск имени

С учетом этого давайте посмотрим, как безотказный поиск имени будет работать для первого примера:

class A {};

class B : private A {};

class C : private B
{
public:
    class D : private A {}; // Error here
};
  • A просматривается в рамках C. Если бы это было определено там, проблем не было бы.
  • Он показывает, что A частным образом унаследован базовым (частным) классом B и, следовательно, выдает ошибку компилятора. Кланг говорит:
    note: constrained by private inheritance here:
    class B : private A {}; 

Опять же, согласно цитате, она должна работать, если вы используете полное имя, как показано на рисунке

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 является неквалифицированным именем и ищет в первую очередь в этой области, он оказывается найденным и недоступным, поэтому ошибка.