57

Suppose I have a class F that should be friend to the classes G (in the global namespace) and C (in namespace A).

  • to be friend to A::C, F must be forward declared.
  • to be friend to G, no forward declaration of F is necessary.
  • likewise, a class A::BF can be friend to A::C without forward declaration

The following code illustrates this and compiles with GCC 4.5, VC++ 10 and at least with one other compiler.

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;
}

To me this seems inconsistent. Is there a reason for this or is it just a design decision of the standard?

pesche
  • 3,054
  • 4
  • 34
  • 35

3 Answers3

54

C++ Standard ISO/IEC 14882:2003(E)

7.3.1.2 Namespace member definitions

Paragraph 3

Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class or function (this implies that the name of the class or function is unqualified) the friend class or function is a member of the innermost enclosing namespace.

// 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
}

Your friend class BF; is a declaration of A::BF in namespace A rather than global namespace. You need the global prior declaration to avoid this new declaration.

Alexey Malistov
  • 26,407
  • 13
  • 68
  • 88
  • 20
    A reasonable person might ask why it does not suffice to write "`friend class ::F;`" (as shown in the OP's code), thus explicitly pushing `F` into the global namespace. I think the answer is along the lines of "a qualified-id never declares a new name" but am not sure exactly what the standard says on this point. – zwol Dec 20 '10 at 19:22
  • 1
    @Zack That was my initial question, too: Why do I need to add a forward declaration, when I move a class in another namespace? But there are a couple of discussions already on this topic (e.g. http://stackoverflow.com/questions/2059665/, http://stackoverflow.com/questions/1368642/). Seems that not needing a forward declaration in the same scope is a convenience that has been granted by the authors of the standard. – pesche Dec 20 '10 at 19:59
  • All this C++ standard is cool but there comes Microsoft with its MSVC compiler where even with conformance mode enabled, you can just write `friend class F` without `::` and you don't even have to forward-declare `class F;` It will compile anyway ¯\_(ツ)_/¯ – Kyriet Jun 08 '23 at 10:28
7

Let's take into account these 3 code lines from your sample:

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++ standard in paragraph 7.3.1.2, point 3 ( Namespace member definitions) says:

The friend declaration does not by itself make the name visible to unqualified lookup (3.4.1) or qualified lookup (3.4.3). [ Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship). —end note ]

And line 2 follows exactly what standard requires.

All confusion is because "friend declaration" is weak, you need to provide solid forward declaration for further usage.

LookAheadAtYourTypes
  • 1,629
  • 2
  • 20
  • 35
2

Because it wouldn't make sense to be able to declare something in the global namespace if you're inside a namespace {} block. The reason friend class BF; works is that it acts like an implicit forward declaration.

Billy ONeal
  • 104,103
  • 58
  • 317
  • 552