13

Is the following example well-formed?

namespace N {
    class A;
}
using namespace N;

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

namespace N {
    class A {
        B m;
        int get() { return m.i; }
    };
}

This example compiled successfully with Clang 3.5, but failed with g++ 4.8.1 with the following:

main.cpp: In member function ‘int N::A::get()’:
main.cpp:7:9: error: ‘int B::i’ is private
     int i;
         ^
main.cpp:14:30: error: within this context
         int get() { return m.i; }
                              ^

C++11 standard §7.3.1.2 p3 says,

If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.

In example, class A is not member of innermost enclosing namespace (i.e. global namespace), but class A is introduced by using directive into global namespace.

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Mitsuru Kariya
  • 938
  • 4
  • 14
  • Your are creating a nested namespace `N` inside N due to the `using namespace N`, try removing the last `namespace N` – ichramm Jan 09 '14 at 19:41
  • 2
    @ichramm `using namespace N` doesn't mean everything that follows implicitly belongs to `namespace N` – Praetorian Jan 09 '14 at 19:43
  • @LightnessRacesinOrbit @Praetorian I wasn't sure until I tested the above code commenting out the last `namespace N', it's weird, I know. – ichramm Jan 09 '14 at 19:47
  • @ichramm: It's not just weird: it's untrue. You're observing something else. – Lightness Races in Orbit Jan 09 '14 at 19:48
  • @LightnessRacesinOrbit I think you were on the right track. The code is valid, but it's not N::A that gets friend benefits. It's a not-yet-existing ::A. That's why it doesn't compile with `get()`. (IMO....) – jrok Jan 09 '14 at 19:49
  • 1
    @ichramm Your experiment is admirable, your conclusion less so. – Alan Stokes Jan 09 '14 at 19:50
  • @jrok: I think that was my conclusion too. But I was bored of see-sawing :( – Lightness Races in Orbit Jan 09 '14 at 19:51
  • Thanks for the clarification, another interesting think is that changing the friend statement with `friend class N::A` seems to fix the problem too. – ichramm Jan 09 '14 at 19:52
  • Interesting note: `using N::A;` instead of `using namespace N;` compiles just fine. I'm willing to classify this as a bug. – Rapptz Jan 09 '14 at 19:52
  • And plain `friend A;` also compiles. – TemplateRex Jan 09 '14 at 19:52
  • [namespace.udir]/2 "During unqualified name lookup, the names appear as if they were declared in the nearest enclosing namespace which contains both the *using-directive* and the nominated namespace." And [basic.lookup.elab]/2 "If the *elaborated-type-specifier* has no *nested-name-specifier*, and unless the *elaborated-type-specifier* appears in a declaration with the following form: *class-key attribute-specifier-seq opt identifier* `;` the identifier is looked up according to 3.4.1 [unqualified name lookup] but ignoring any non-type names that have been declared." – dyp Jan 09 '14 at 20:02
  • I think the quoted §7.3.1.2 p3 only states that namespaces enclosing the innermost one are not searched. But the using-directive lets name lookup find the names as if they were declared in the innermost enclosing namespace (here). – dyp Jan 09 '14 at 20:03

2 Answers2

9

To make N::A without qualification a friend of B you'd use

friend A;

rather than

friend class A;

When using an elaborated type specifier, i.e., class A, and it is in this specific form, it introduces a class name (see 3.4.4 [basic.lookup.elab] paragraph 2).

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • The form mentioned in [basic.lookup.elab]/2 does not contain the `friend` as far as I can see. So the form mentioned there is a part of the friend-declaration. Does it still apply? – dyp Jan 09 '14 at 20:12
  • @dyp: yes, I'm aware. However `friend class A;` _does_ match the expression ".* _class-key attribute-specifier-seq? identifier;_". I think the intent is to distinguish it from uses like `class foo f();` or `int stat(struct stat*)`, etc. and I think it does apply. – Dietmar Kühl Jan 09 '14 at 20:15
1

While using namespace N is pulling the name N::A into the global namespace, it is not declaring that A in the global namespace. Hence an additional A in the global namespace is the friend of B. clang is wrong.

  • @Diether C++11 standard 7.3.4 p2 says "During unqualified name lookup (3.4.1), the names appear **as if** they were declared ...", and 3.4.1 p2 says "For the purpose of the unqualified name lookup rules described in 3.4.1, the declarations from the namespace nominated by the *using-directive* **are considered members of that enclosing namespace**". – Mitsuru Kariya Jan 10 '14 at 04:38
  • 1
    @Diether: Thanks! You are very right! I found issue [N1229](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2000/n1229.html). However, I think that C++11 standard should show this situation more clearly. – Mitsuru Kariya Jan 10 '14 at 06:52