Защищенные члены базовых классов недоступны для друзей производных классов - PullRequest
6 голосов
/ 12 февраля 2020

Стандарт C ++ в [class.access / 1] (выделено мной):

Членом класса может быть

  • частный; то есть его имя может использоваться только членами и друзьями класса, в котором оно объявлено.
  • protected; то есть его имя может использоваться только членами и друзьями класса, в котором он объявлен, классами, производными от этого класса, и их друзьями (см. [class.protected]).
  • публично; то есть его имя может использоваться где угодно без ограничения доступа.

Так почему компилятор вызывает эту ошибку в следующей программе C ++?

#include <iostream>

class B {
  protected:
    static int const i = 1;
};

class D: public B {
  public:
    void f();
    friend void g();
};

void D::f() {
  B b;
  std::cout << b.i;  // OK
}

void g() {
  B b;
  std::cout << b.i;  // error: 'i' is a protected member of 'B'
}

int main() {
  D d;
  d.f();
  g();
  return 0;
}

Обратите внимание, что защищенный элемент данных B::i объявляется stati c и не подлежит дальнейшим ограничениям, определяемым c для защищенных non-stati c членов в [class.access / class.protected-1] , что также вызовет ту же ошибку при доступе b.i в функции-члене D::f, что и в функции g.

Примечание. - Я использую C ++ 17 на компиляторе Clang 9.0.0.

1 Ответ

3 голосов
/ 12 февраля 2020

Хорошо, похоже, это проблема спецификации. Часть, которую я цитировал, не совпадает с c с другой частью, которая задает новое правильное поведение, за которым следует Clang (но не G CC или MSV C на удивление), [class.access / base- 5] (выделено мной):

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

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

Здесь друг не упоминается для производного класса. Но раньше. Он был удален в C ++ 17 из-за сообщения о дефекте CWG 1873 (выделено мной):

Защищенный доступ к элементу от друзей производного класса

Раздел: 14.2 [ class.access.base] Статус: CD4 Автор: Ричард Смит Дата: 2014-02-18

[Перемещено в DR на совещании в мае 2015 года.]

Согласно 14.2 [класс. access.base] пункт 5,

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

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

Предоставление доступа через класс P вызывает беспокойство. По крайней мере, должно быть ограничение, чтобы P был виден в R. В качестве альтернативы, эта часть правила может быть удалена полностью; это положение, по-видимому, широко не используется в существующем коде, и такие ссылки можно легко преобразовать в использование P вместо N для именования члена.

Примечания с собрания в июне 2014 года:

CWG отметила, что удаление положения о друге приведет к нежелательной асимметрии между функциями-членами P и его друзьями. Вместо этого намерение состоит в том, чтобы требовать, чтобы P был полным типом в R.

Примечания от собрания в ноябре 2014 года:

Хотя асимметрия неудачная, разница между ссылками в элементе Функция и ссылка в другом состоит в том, что функция-член конкретно определяет, какой P находится в поле зрения, в то время как другу может быть подружен класс, который является специализацией шаблона класса и, следовательно, потребуются экземпляры, которые в противном случае не возникли бы. Таким образом, CWG решила просто исключить дело друга.

Предлагаемое решение, ноябрь 2014 года:

Изменить маркер 5.3 из 14.2 [class.access.base] следующим образом:

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

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

Я подал на GitHub запрос на удаление, чтобы исправить это несоответствие: https://github.com/cplusplus/draft/pull/3672

...