Доступ к защищенному конструктору базового класса

Производный класс может вызывать защищенный конструктор базового класса в его инициализаторе ctor, но только для своего собственного подобъекта базового класса, а не в другом месте:

class Base {
  protected:
    Base() {}
};

class Derived : Base {
  Base b;
  public:
    Derived(): Base(),    // OK
               b() {      // error
        Base b2;          // error
    }
};

Что говорит об этом стандарт? Здесь [class.protected]/1:

Дополнительная проверка доступа за пределы, описанная выше в разделе 11, применяется, когда нестатические данные член или нестатическая функция-член является защищенным членом его класса именования (11.2). Как описано ранее доступ к защищенному члену предоставляется, поскольку ссылка встречается у друга или члена какого-либо класс C. Если доступ заключается в формировании указателя на член (5.3.1), спецификатор вложенного имени должен обозначать C или класс, полученный из C. Все остальные обращения включают (возможно неявное) выражение объекта (5.2.5). В этом случае, класс выражения объекта должен быть C или класс, полученный из C. [Пример:...

Существует ли выражение объекта при вызове конструктора? Нет, не так ли? Итак, где в стандарте описано управление доступом для конструкторов защищенного базового класса?

Ответ 1

Доступ к protected применяется только к родительским членам вашего собственного текущего типа объекта. Вы не получаете открытого доступа к защищенным членам других объектов родительского типа. В вашем примере вы получите доступ к базовому конструктору по умолчанию как часть Derived, а не к автономному объекту как b.

Позвольте сложить цитату, опубликованную вами из стандарта (11.4/1). Предположим, что C в стандарте соответствует вашему классу Derived:

Дополнительная проверка доступа за пределами описанных выше в пункте 11 применяется, когда нестатический элемент данных или нестатическая функция-член является защищенным членом его класса именования (11.2).

Таким образом, конструктор базового класса фактически представляет собой non-static member function его класса именования (b), поэтому этот раздел применяется до сих пор.

Как описано выше, доступ к защищенному члену предоставляется, поскольку ссылка встречается у друга или члена некоторого класса C.

Член (конструктор) C, поэтому мы все еще хороши здесь.

Если доступ заключается в формировании указателя на элемент (5.3.1), inest-name-specifier должен обозначать C или класс, полученный из C.

Это не указатель на член, поэтому это не относится.

Все другие обращения включают (возможно неявное) выражение объекта (5.2.5).

Затем стандарт утверждает, что все другие возможные обращения должны включать выражение объекта.

В этом случае класс выражения объекта должен быть C или классом полученный из C.

Наконец, в стандарте указано, что класс выражения должен быть C или еще один производный класс. В этом случае ваше выражение Base() на самом деле является C, вызывая родительский конструктор (подумайте об этом как this->Base(). Выражение b явно имеет тип Base (что явно объявленный тип элемента b, подумайте о this->b->Base()). Теперь мы проверим: Is Base a C или дочерний элемент C? Это не так, поэтому код не является законным.

Ответ 2

С++ 11 §11.2/5:

"
Член m доступен в точке R, когда он назван в классе N, если

  • m как член N является общедоступным или

  • m как член N является закрытым, а R встречается у члена или друга класса N или

  • m как член N защищен, а R встречается у члена или друга класса N, или в члене или другом класса Pот N, где m как член P является общедоступным, приватным или защищенным, или

  • существует базовый класс B of N, доступный в R, и m доступен в R при имени в классе B.

Для вашего вызова конструктора

Base b2;

применяется пункт 3 rd. m - конструктор Base. N, класс именования - Base. m как член Base защищен, и объявление происходит в члене класса Derived, производном от Base, но это не тот случай, когда конструктор Base как член Derived является общедоступный, закрытый или защищенный: он просто не является членом Derived, конструкторы не неявно унаследованы.

Я думаю, что язык "публичный, частный или защищенный" довольно неудобен; Я могу только предположить, что это результат некоторой эволюции этого параграфа.

Мне еще предстоит найти объяснение того, насколько формально защищенный конструктор Base доступен в списке инициализаторов членов в Derived, но затем я начал изучать это для этого вопроса.


Обновление: я не могу найти какой-либо язык в стандарте, относящийся к доступу к конструкторам в списке инициализаторов, и я не могу найти отчет об ошибках. Вероятно, это дефект.