Intereting Posts
как генерировать некоррелированные случайные последовательности с использованием c ++ Конфликт версии Protobuf при использовании Opencv и Tensorflow c ++ boost :: variant – почему «const char *» преобразуется в «bool»? Размещение новой проблемы Почему стандартные контейнеры используют шаблоны функций вместо не-шаблонных операторов Koenig Ошибка GCC: нельзя применять offsetof для функции-члена MyClass :: MyFunction Могут ли шаблоны использоваться для доступа к структурным переменным по имени? Как плагин изменяет размер windows Проверка плавающей запятой std :: vector с C ++ Catch Где функции GPU на OpenCV 3.0? Можно использовать std :: deque :: push_back () и std :: deque :: front () / std :: deque :: pop_front () для разных streamов без синхронизации? Сочетание комбинированных клавиш multi_index с использованием MEM_FUN Visual Studio необходимо перестроить решение для каждого изменения зачем ставить десятки * перед указателем функции или функцией, возвращающей указатель функции? c ++ тернарный оператор

Как добраться до основания C ++

Стандарт C ++ 2011 года гласит в разделе 11.4, что

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

(В старом стандарте аналогичная формулировка указана в разделе 11.5.)

Это правило ограничивает часто повторяющуюся идею о том, что «защищенные члены B могут быть доступны любым B или производным classом B.» Однако интерпретация правила сложна, о чем свидетельствует тот факт, что разные компиляторы текущего времени применяют правило по-разному.

См., Например, этот тестовый код. Я скомпилировал этот код с помощью Apple LLVM Compiler 4.1, GCC 4.7.2 и Visual Studio 2010. Есть сходства, а также различия в сообщениях об ошибках.

class Base { protected: int A; }; class Derived : public Base { protected: int B; }; class Grandchild : public Derived { void access_protected(Base* b, Derived* d, Grandchild* g, class GreatGrandchild* gg ); }; class GreatGrandchild : public Grandchild {}; void Grandchild::access_protected(Base* b, Derived* d, Grandchild* g, GreatGrandchild* gg ) { int* p; Base lb; Derived ld; Grandchild lg; GreatGrandchild lgg; A = 1; // Legal... B = 2; Base::A = 1; Derived::B = 2; b->A = 1; // Illegal ALL p = &(b->A); // Illegal ALL lb.A = 1; // Illegal ALL p = &(lb.A); // Illegal ALL d->A = 1; // Illegal GCC, VS p = &(d->A); // Illegal GCC, VS ld.A = 1; // Illegal GCC, VS p = &(ld.A); // Illegal GCC, VS d->B = 2; // Illegal ALL p = &(d->B); // Illegal ALL ld.B = 2; // Illegal ALL p = &(ld.B); // Illegal ALL g->A = 1; // Legal... g->B = 2; lg.A = 1; lg.B = 2; gg->A = 1; gg->B = 2; lgg.A = 1; lgg.B = 2; } 

Из этих результатов я получаю следующее: (1) всегда доступно доступ к защищенным членам вашего собственного classа и вашим производным classам; (2) всегда запрещено доступ к защищенным членам базового classа, в котором они были объявлены, кроме этого classа; (3) хотя стандарт тщательно различает указатели на члены и «выражения объектов», как стандартные, так и составители дают им те же ограничения; (4) неясно, является ли законным доступ к защищенному члену «промежуточного» базового classа ( Derived в примере), для которого член был объявлен в базе промежуточного.

Тогда путаница заключается в том, хорошо ли мне говорить о защищенном члене моего дедушки, принадлежащем моему родителю. Не предусмотрено двойное намерение.

(Я игнорирую friends за простоту и здравомыслие).

Поскольку protected является такой фундаментальной частью языка, я мотивирован, чтобы хорошо ее понять. Пожалуйста:

  1. Основываясь на вашей интерпретации стандарта, какой компилятор правильно это реализует?
  2. В чем обоснование общего ограничения? Почему я не могу свободно обращаться к защищенным членам базовых classов? Какая конкретная ошибка заключается в том, чтобы избежать этого? Знаете ли вы о онлайн-обсуждении – в идеале, как это принято в комитете по стандартизации, – который исследует это обоснование?

Я бы сказал, что GCC и Visual Studio верны.

Учитывая следующую ситуацию:

 class Base { protected: int A; }; class Derived : public Base { protected: int B; }; class OtherGrandchild : Derived { }; class Grandchild : Derived { void access_protected(OtherGrandchild* otherGrandchild); }; void Grandchild::access_protected(OtherGrandchild* otherGrandchild) { otherGrandchild->A = 1; // Should be illegal otherGrandchild->B = 1; // Should be illegal Derived* derived = static_cast(otherGrandchild); derived->A = 1; // Should still be illegal derived->B = 1; // Should still be illegal } 

Если вы не были ограничены, вы могли бы изменить других частных участников OtherGrandchild от Grandchild просто OtherGrandchild их в общий базовый тип. Такой доступ должен быть разрешен только через объявления друзей.

Я не уверен в каких-либо дискуссиях по этой теме, но это будет моя интерпретация.

Причиной этого является то, что производный объект D может получить доступ только к защищенным членам базы, используя выражение объекта, которое является либо D, либо другим classом Derived from D.

Стандарт не позволяет D получить доступ к защищенным членам другого объекта, используя выражение объекта типа B

Это то, что точно (ОК примерно), что говорит выше цитата.

 class base { protected: int x; }; class derived : public base { public: void f(base *p) { x = 2; // good p->x = 3; // not good. base is not 'derived' nor derived from 'derived' } }; int main() { } 

Подумайте об этом на мгновение. Публичный член базы B доступен в любом производном classе D. Частные члены B недоступны в любом производном classе D. Только защищенные члены требуют некоторого рассмотрения. И приведенная выше цитата из Стандарта излагает это соображение. Защищенные нестатические элементы B могут быть доступны в производном classе D только с использованием выражения объекта, которое имеет тип D или тип, полученный дальше от D

Я не могу ссылаться на ссылочный или авторитетный источник, но мне кажется, что g ++ и VS являются правильными здесь (впервые они согласны?), Потому что он просто не делает логического смысла, что пустой промежуточный проиндексированный class может изменить контроль доступа защищенных данных родителя.

Что касается обоснования, это почти наверняка, потому что менее ограничительный, который вы делаете, protected больше, чем public он становится (он уже имеет много общего с публикой в ​​том, что вам просто нужно получить class, чтобы получить неограниченный доступ к внутренним родителям). Если вы начнете разрешать дочерние classы, которые даже не совпадают с вашим экземпляром, чтобы управлять вашим состоянием, вероятность нарушения инвариантов classа значительно возрастает.