Ответ 1
Человеческий разговор
Начну с описания того, что здесь происходит. Простите меня, если вы уже знаете это, но он создает необходимый контекст для последующих действий.
Компилятор разрешает неквалифицированный A
до ::C::A
(результат будет таким же, если вы сами сделаете изменение на уровне источника). Поскольку ::C::A
недоступен, выводится сообщение об ошибке.
Вы предлагаете, чтобы компилятор обнаружил, что ::C::A
недоступен, а ссылка на A
должна считаться ссылкой на ::A
как резерв. Однако ::C::A
и ::A
могут быть легко двумя совершенно разными.
Автоматическое угадывание того, что должно быть сделано здесь, не только подвержено появлению ошибок и/или вытягиванию волос¹, но и полностью противоречит духу С++.
Standardese
Подтверждение того, что это поведение является согласованным и по-дизайну, непосредственно из стандарта С++ 11.
В § 9/2 говорится:
Имя класса вставляется в область, в которой она объявлена сразу после просмотра имени класса. Название класса также вставлен в сферу действия самого класса; это известно как впрыскивается класса имя.
Это означает, что внутри области класса C
A
- это имя введенного класса.
В §3.4/3 указано, что имя введенного класса является кандидатом на поиск имен:
Введенное класс-класс класса также считается членом этого класса для целей скрытия имени и поиска.
В §3.4/1 разъясняется, что недоступность базы A
не препятствует рассмотрению вложенного класса A
:
Правила доступа рассматриваются только после поиска и функции имени разрешение перегрузки (если применимо) выполнено.
§11.1/5 дает прямое объяснение конкретной ситуации:
[Примечание: в производном классе поиск имени базового класса найдет имя введенного класса вместо имени базового класса в область, в которой она была объявлена. Имя введенного класса может быть меньше доступный, чем имя базового класса в области, в которой он был объявлен. -end note]
Стандарт также дает этот пример, который эквивалентен вашему:
class A { };
class B : private A { };
class C : public B {
A *p; // error: injected-class-name A is inaccessible
::A *q; // OK
};
¹ Представьте, что произойдет, если A
изначально является базой public
, а затем становится private
во время рефакторинга. Также представьте, что ::A
и ::C::A
не связаны. Вы ожидаете, что вызов типа a->foo()
(который использовался для работы) потерпит неудачу, потому что foo
больше не доступен, но вместо этого тип A
изменился за вашей спиной, и теперь вы получаете "есть no method foo
". А?!? И это, конечно, далеко от худшего, что могло случиться.