Why does a C++ friend class need a forward declaration only in other namespaces?

C++NamespacesFriendForward Declaration

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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 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.

Solution 2 - C++

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.

Solution 3 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpescheView Question on Stackoverflow
Solution 1 - C++Alexey MalistovView Answer on Stackoverflow
Solution 2 - C++LookAheadAtYourTypesView Answer on Stackoverflow
Solution 3 - C++Billy ONealView Answer on Stackoverflow