Почему классу-другу C ++ требуется предварительное объявление только в других пространствах имен? - PullRequest
46 голосов
/ 20 декабря 2010

Предположим, у меня есть класс F, который должен дружить с классами G (в глобальном пространстве имен) и C (в пространстве имен A).

  • , чтобы бытьдруг A::C, F должен быть объявлен форвардом.
  • , чтобы быть другом G, форвардное объявление F не требуется.
  • аналогично, класс A::BF может быть другом A::C без предварительного объявления

Следующий код иллюстрирует это и компилирует с GCC 4.5, VC ++ 10 и, по крайней мере, с одним другим компилятором.

class G {
    friend class F;
    int g;
};

// without this forward declaration, F can't be friend to A::C
class F;

namespace A {

class C {
    friend class ::F;
    friend class BF;
    int c;
};

class BF {
public:
    BF() { c.c = 2; }
private:
    C c;
};

} // namespace A

class F {
public:
    F() { g.g = 3; c.c = 2; }
private:
    G g;
    A::C c;
};

int main()
{
    F f;
}

Мне это кажется противоречивым.Есть ли для этого причина или это просто дизайнерское решение стандарта?

Ответы [ 3 ]

45 голосов
/ 20 декабря 2010

C++ Стандарт ISO/IEC 14882:2003(E)

7.3.1.2 Определения элементов пространства имен

Пункт 3

Каждое имя, впервые объявленное в пространстве имен, является членом этого пространства имен .Если объявление друга в нелокальном классе сначала объявляет класс или функцию (это означает, что имя класса или функции является неквалифицированным), класс или функция друга является членом внутреннего вложенного пространства имен.

// Assume f and g have not yet been defined.
void h(int);
template <class T> void f2(T);
namespace A {
   class X {
   friend void f(X);  //  A::f(X) is a friend
      class Y {
         friend void g();  //  A::g is a friend
         friend void h(int);  //  A::h is a friend
         //  ::h not considered
         friend void f2<>(int);  //  ::f2<>(int) is a friend
      };
   };
   //  A::f, A::g and A::h are not visible here
   X x;
   void g() { f(x); }  // definition of A::g
   void f(X) { /* ... */}  // definition of A::f
   void h(int) { /* ... */ }  // definition of A::h
   //  A::f, A::g and A::h are visible here and known to be friends
}

Ваш friend class BF; является объявлением A::BF в пространстве имен A, а не в глобальном пространстве имен.Вам нужно глобальное предварительное объявление, чтобы избежать этого нового объявления.

5 голосов
/ 06 августа 2014

Давайте учтем эти 3 строки кода из вашего примера:

1. friend class F; // it creates "friend declaration", (that's not the same as ordinary forward declaration

2. class F; // without this forward declaration, F can't be friend to A::C <-- this is ordinary forward declaration

3. friend class ::F; // this is qualified lookup (because of ::), so it needs previous declaration, which you provide in line 2.

Стандарт C ++ в пункте 7.3.1.2, пункт 3 (определения членов пространства имен) говорит:

Объявление друга само по себе не делает имя видимым для неквалифицированный поиск (3.4.1) или квалифицированный поиск (3.4.3). [ Обратите внимание имя друга будет видно в его пространстве имен, если совпадающее объявление предоставляется в области имен (до или после определение класса предоставления дружбы). —Конечная записка]

И строка 2 точно соответствует требованиям стандарта.

Вся путаница заключается в том, что «объявление друга» является слабым , вам необходимо предоставить твердое предварительное объявление для дальнейшего использования.

2 голосов
/ 20 декабря 2010

Потому что не имеет смысла иметь возможность объявить что-либо в глобальном пространстве имен, если вы находитесь внутри блока namespace {}. Причина, по которой friend class BF; работает, заключается в том, что он действует как неявное прямое объявление.

...