14 Member access control [class.access]

14.3 Friends [class.friend]

Друг класса - это функция или класс, которому дано разрешение на использование частных и защищенных имен членов из класса. Класс указывает своих друзей, если таковые имеются, посредством объявления друзей. Такие объявления предоставляют друзьям особые права доступа, но они не делают назначенных друзей членами класса дружбы. [ Example: Следующий пример иллюстрирует различия между участниками и друзьями:

class X {
  int a;
  friend void friend_set(X*, int);
public:
  void member_set(int);
};

void friend_set(X* p, int i) { p->a = i; }
void X::member_set(int i) { a = i; }

void f() {
  X obj;
  friend_set(&obj,10);
  obj.member_set(10);
}

end example]

Объявление класса другом подразумевает, что к именам закрытых и защищенных членов класса, предоставляющего дружбу, можно получить доступ в base-specifiers объявлениях членов и дружественного класса. [Example:

class A {
  class B { };
  friend class X;
};

struct X : A::B {               // OK: A​::​B accessible to friend
  A::B mx;                      // OK: A​::​B accessible to member of friend
  class Y {
    A::B my;                    // OK: A​::​B accessible to nested member of friend
  };
};

end example] [Example:

class X {
  enum { a=100 };
  friend class Y;
};

class Y {
  int v[X::a];                  // OK, Y is a friend of X
};

class Z {
  int v[X::a];                  // error: X​::​a is private
};

end example]

Класс не должен определяться в объявлении друга. [Example:

class A {
  friend class B { };           // error: cannot define class in friend declaration
};

end example]

friend Заявление , что не объявляет функцию должно иметь одну из следующих форм:

friend elaborated-type-specifier ;
friend simple-type-specifier ;
friend typename-specifier ;

[ Декларация может быть в (п , ). ] Если спецификатор типа в объявлении обозначает тип класса (возможно, квалифицированный cv), этот класс объявляется как друг; в противном случае объявление игнорируется. [Note: friend declarationtemplate-declaration [temp] [temp.friend]end note friend friend Example:

class C;
typedef C Ct;

class X1 {
  friend C;                     // OK: class C is a friend
};

class X2 {
  friend Ct;                    // OK: class C is a friend
  friend D;                     // error: no type-name D in scope
  friend class D;               // OK: elaborated-type-specifier declares new class
};

template <typename T> class R {
  friend T;
};

R<C> rc;                        // class C is a friend of R<C>
R<int> Ri;                      // OK: "friend int;" is ignored

end example]

Функция, впервые объявленная в объявлении друга, имеет связь с пространством имен, членом которого она является ([basic.link]). В противном случае функция сохраняет свою предыдущую связь ([dcl.stc]).

Когда friend объявление относится к перегруженному имени или оператору, только функция, указанная типами параметров, становится другом. Функция-член класса X может быть другом класса Y. [Example:

class Y {
  friend char* X::foo(int);
  friend X::X(char);            // constructors can be friends
  friend X::~X();               // destructors can be friends
};

end example]

Функция может быть определена в объявлении друга класса тогда и только тогда, когда класс является нелокальным классом ([class.local]), имя функции неквалифицировано и функция имеет область пространства имен. [Example:

class M {
  friend void f() { }           // definition of global f, a friend of M,
                                // not the definition of a member function
};

end example]

Такая функция неявно является inline function. friend Функция , определенная в классе находится в (лексической) области видимости класса , в котором она определена. Дружественная функция, определенная вне класса, не ([basic.lookup.unqual]).

Нет storage-class-specifier не должно фигурировать в decl-specifier-seq декларации друга.

Имя, назначенное объявлением друга, должно быть доступно в области действия класса, содержащего объявление друга. Смысл объявления друга одинаков, независимо от того, присутствует ли объявление друга в части классаprivate, protected или public ([class.mem]) member-specification.

Дружба не является ни наследственной, ни переходной. [Example:

class A {
  friend class B;
  int a;
};

class B {
  friend class C;
};

class C  {
  void f(A* p) {
    p->a++;         // error: C is not a friend of A despite being a friend of a friend
  }
};

class D : public B  {
  void f(A* p) {
    p->a++;         // error: D is not a friend of A despite being derived from a friend
  }
};

end example]

Если объявление друга появляется в a, local class а указанное имя является неквалифицированным именем, предыдущее объявление ищется без учета областей, которые находятся за пределами самой внутренней охватывающей неклассовой области. Для объявления дружественной функции, если нет предварительного объявления, программа имеет неправильный формат. Для объявления дружественного класса, если предшествующего объявления нет, указанный класс принадлежит к самой внутренней охватывающей неклассовой области, но если на него впоследствии будет сделана ссылка, его имя не будет найдено с помощью поиска по имени до тех пор, пока соответствующее объявление не будет предоставлено в самая внутренняя охватывающая неклассовая область видимости. [Example:

class X;
void a();
void f() {
  class Y;
  extern void b();
  class A {
  friend class X;   // OK, but X is a local class, not ​::​X
  friend class Y;   // OK
  friend class Z;   // OK, introduces local class Z
  friend void a();  // error, ​::​a is not considered
  friend void b();  // OK
  friend void c();  // error
  };
  X* px;            // OK, but ​::​X is found
  Z* pz;            // error, no Z is found
}

end example]