Должны ли друзья быть переходными во вложенных классах? - PullRequest
5 голосов
/ 21 июня 2011
class private_object
{
private:
  struct make_public;
  friend struct make_public;
  static void method1() {}
};

struct private_object::make_public
{
  class nested_outer
  {
    void callFromOuter() 
    { private_object::method1(); }   // Should this be an error?

    class nested_inner
    {
      void callFromInner() 
      { private_object::method1(); } // How about this one?
    };
  };
};

Эта проблема дружбы возникла, когда я пытался перенести проект с открытым исходным кодом для компиляции под Borland.Согласно parashift и двум смежным вопросам здесь и здесь приведенный выше пример не должен быть действительным.

Однако после тестирования на семи различных компиляторах 1 жаловались только borland и dmc.Такое поведение меня удивило, потому что я не ожидал, что дружба будет переходной в вложенных классах.

Так что возникает пара вопросов:

  • Какое поведение правильное?Я предполагаю, что это приемлемо для большинства компиляторов.
  • Если это правильное поведение, почему этот случай транзитивности дружбы в порядке?
  • Если это правильно, то это также подразумеваетизменить в стандарте.Какие могут быть причины для разрешения этого в стандарте?
  • Для компиляторов, которые отклонили этот код, какой будет подходящий обходной путь?Имейте в виду, что в реальном проекте может быть достаточно глубокое вложение, поэтому я ищу решение, которое можно масштабировать.

1.протестировано на mingw-gcc 4.5.2, clang, borland c ++ builder2007, digital mars, open watcom, visualc2010 и comeau online

Ответы [ 2 ]

3 голосов
/ 21 июня 2011

В C ++ 03 вложенный класс не может получить доступ к private и protected членам включающего класса по умолчанию (см. §11.8 / 1). Но если вы сделаете их друзьями окружающего класса, они смогут получить к ним доступ. Но опять же, вложенный класс вложенного класса все еще не является другом внешнего вложенного класса, вложенный класс вложенного класса не может получить доступ к закрытым и защищенным членам самого внешнего включающего класса; он не может даже получить доступ к закрытому и защищенному члену ближайшего окружающего класса, как отмечалось ранее. То, что вы делаете, это то, что не допускается.

Стандарт C ++ (2003) говорит в $ 11,8 / 1 [class.access.nest],

Члены вложенного класса не имеют особый доступ к членам включающий класс , ни к классам или функции, которые подарили дружбу вмещающему классу; обычный правила доступа (пункт 11) повиновался. Члены огороженного класс не имеет специального доступа к члены вложенного класса; обычный правила доступа (пункт 11) повиновался.

Пример из самого стандарта:

class E 
{
    int x;
    class B { };
    class I 
    {
        B b; // error: E::B is private
        int y;
        void f(E* p, int i)
        {
           p->x = i; // error: E::x is private
        }
   };
   int g(I* p)
   {
       return p->y; // error: I::y is private
   }
};

Это дефект в стандарте C ++ 03.

Кстати, это дефект в стандарте C ++ 03. Поскольку вложенный класс является членом, он должен иметь доступ к закрытым и защищенным элементам, как и любой другой элемент:

§9.2 / 1 (C ++ 03):

Членами класса являются члены данных, функции-члены (9.3), вложенные типы … Вложенные типы - это классы (9.1, 9.7) и перечисления (7.2), определенные в классе…

См. Отчет о дефектах :

1 голос
/ 21 июня 2011

Мне почему-то кажется, что это должно быть разрешено . Хотя я не уверен насчет стандартного поведения. Кто-то может указать.

Для компиляторов, которые отклонили этот код, что было бы уместно обходной путь?

Лучшее, что я могу придумать, это сделать упаковку:

struct private_object::make_public
{
  static void wrap_method1() { private_object::method1(); }
  // use wrap_method1() everywhere else
  // ...
};
...