17

Trying to make a class friends with an extern "C" function, this code works:

#include <iostream>

extern "C" {
  void foo();
}

namespace {
  struct bar {
    // without :: this refuses to compile
    friend void ::foo();
    bar() : v(666) {}
  private:
    int v;
  } inst;
}

int main() {
  foo();
}

extern "C" {
  void foo() {
    std::cout << inst.v << std::endl;
  }
}

But I was very surprised to find that with g++ 4.6.1 and 4.4.4 I have to explicitly write :: in friend void ::foo(); otherwise the friendship doesn't work. This :: is only needed when it's extern "C" though.

  1. Is this a compiler bug/problem? I wasn't expecting that behaviour.
  2. If it isn't a bug why is this required, but only when it's extern "C" and not without it? What about the name lookup rules changes that makes this necessary?

I'm stumped. There is probably some rule for this that I can't find.

Flexo
  • 87,323
  • 22
  • 191
  • 272
  • [Not the case without the anonymous namespace.](http://codepad.org/Hw7JC8Ky) And [here](http://codepad.org/9qVdlrhC)'s the testcase. – Lightness Races in Orbit Oct 18 '11 at 15:09
  • Hmm is that a bug then? I can't find anything in the lookup rules that would explain why that combination of `extern "C"` and anonymous `namespace` would need the `::` but removing either makes it unnecessary. Admittedly though my knowledge of the finer details of name lookup is hazy and this was based on searching. – Flexo Oct 18 '11 at 15:12
  • @TomalakGeret'kal - it fails with any `extern "C"` + `namespace`, not just anonymous ones. – Flexo Oct 18 '11 at 15:13
  • What if you say `friend extern "C" void foo();`? – Kerrek SB Oct 18 '11 at 15:15
  • 4
    btw: I tried with gcc 4.4.0 4.4.1 4.4.2 4.4.3 4.4.4 4.4.5 4.4.6 4.5.0 4.5.1 4.5.2 4.5.3 4.6.0 4.6.1 4.7.0 and the all compile with `::` and fail without. – PlasmaHH Oct 18 '11 at 15:15
  • @KerrekSB - `friend extern "C" void foo();` gives `error: expected unqualified-id before string constant` at that line. – Flexo Oct 18 '11 at 15:16
  • @PlasmaHH - comeau agress with GCC to. – Flexo Oct 18 '11 at 15:19
  • 1
    Point 2 seems wrong. [The code doesn't compile for me even when `foo` is not `extern "C"`.](http://codepad.org/b42SzZkx) – Lightness Races in Orbit Oct 18 '11 at 15:25
  • @TomalakGeret'kal - you're right, I must have made a mistake testing it. Thanks for the useful 7.3.1.2/3 reference though, I was looking for `extern` everywhere in the document to try and explain it. I guess it shows how often I end up making friends with anything outside of the current `namespace`. – Flexo Oct 18 '11 at 15:28
  • 1
    @PlasmaHH deserved more than one measly upvote for testing with 14 toolchains, people c'mon! (even if he does script it \*ahem\*) – Lightness Races in Orbit Jan 15 '13 at 01:28

1 Answers1

14

[n3290: 7.3.1.2/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 the friend class or function is a member of the innermost enclosing namespace. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) 95) this implies that the name of the class or function is unqualified. until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). 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. [..]

The innermost enclosing namespace is the anonymous one, and you didn't qualify the function name, so the name is not found.

The namespace need not be anonymous, either.

Note that the extern "C" in the question is a red herring, as the following also fails for the same reason:

void foo();

namespace {
struct T {
   friend void foo();

   private: void bar() { cout << "!"; }
} t;
}

void foo() { t.bar(); }

int main() {
   foo();
}

/*
In function 'void foo()':
Line 7: error: 'void<unnamed>::T::bar()' is private
compilation terminated due to -Wfatal-errors.
*/

[alternative testcase, adapted from your original code]

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055