Должны ли друзья быть транзитивными во вложенных классах?

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


person greatwolf    schedule 21.06.2011    source источник


Ответы (2)


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

Стандарт С++ (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
   }
};

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

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

§9.2/1 (C++03):

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

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

person Nawaz    schedule 21.06.2011

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

Что может быть подходящим обходным путем для компиляторов, которые отвергли этот код?

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

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