12

Consider this code:

namespace foo {}

class A
{
   class B
   {
   };

   friend int foo::bar( B& );
};

namespace foo
{
   int bar( A::B& )
   {
   }
}

G++ 4.4.3 tells me:

friendfun-innerclass.cpp:21: error: 'int foo::bar(A::B&)' should have been declared inside 'foo'

But I can't declare:

namespace foo
{
   int bar( A::B& );
}

before the class A definition because A::B hasn't been declared. And I can't declare "class A::B" obviously, to declare class B I have to give the definition of class A, and as far as I know the "friend" declarations have to be inside the definition of class A.

What's strange to me is that if I take function "bar()" out of namespace foo everything works fine. It seems counterintuitive to me that having a function inside a namespace or not inside a namespace changes whether the compiler will accept a friend function declaration in the class.

Does anybody know of a way to proprerly structure all the declarations and such to get this to work?

Jay Walker
  • 399
  • 1
  • 10
  • There's probably no way to do exactly what you want. But you can make `bar` a static member of some class in namespace `foo`, and friend that class instead. Close enough. – n. m. could be an AI Oct 11 '11 at 15:42
  • If the function is applied to an object of type `::A::B`, why is it defined in a different namespace `foo`? Does it really make sense to move to a separate namespace a function that is so closely related to both `A` (friend) and `A::B` (argument to the function)? – David Rodríguez - dribeas Oct 11 '11 at 16:46
  • Related: [Passing anonymous classes to private member functions](http://stackoverflow.com/questions/7181809/passing-anonymous-classes-to-private-member-functions) – James McNellis Oct 11 '11 at 17:51

5 Answers5

3

Can't be done the way you want to, because you would have to forward declare a nested class (which you can't) in order to provide a prototype for foo::bar.

As a first attempt to get around this problem, I would probably resort to making foo::bar a function template. That way the compiler will resolve the types after A and B are known.

Test harness:

namespace foo
{
    template<class B> int bar(B&);
};

class A
{
   class B
   {
       template<class B> friend int foo::bar( B& );
       int n_;
   public:
       B() : n_(42) {}
   };

public:
    B b_;
};

template<class B> int foo::bar(B& b)
{
    return b.n_;
}

int main()
{
    A a;
    foo::bar(a.b_);
}
Community
  • 1
  • 1
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Thanks! This is the only solution that really works for the situation I have. The "foo::bar" function is heavily overloaded to operate on many different types so I can't just restructure things to put the functionality somewhere else as some have suggested. – Jay Walker Oct 12 '11 at 17:00
0

I believe that you need ::foo::bar.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • Hmm, I tried changing to "class A { ...; friend int ::foo::bar( B& ); }" which is what I think you're suggesting, but I still get the same error. – Jay Walker Oct 11 '11 at 15:37
  • VC10 still complains `'bar' : is not a member of 'foo'` – Praetorian Oct 11 '11 at 15:38
  • No, at the point of the `friend` declaration, `foo::bar` is not yet known. The fundamental problem is providing a declaration for `foo::bar` which takes a yet-undefined nested class as a parameter. – John Dibling Oct 11 '11 at 15:48
0

You can't because you can't forward-declare inner classes.

This is a close as you're gonna get.

namespace foo {
    class A_B;
    int bar(A_B &);
}

struct A
{
   class B
   {
   };

   friend int foo :: bar (A_B&);
};

namespace foo
{
   struct A_B : public A :: B {
     // constructors, delegated if you're C++11 :-)
   };

   int bar (A_B &)
   {
   }
}

You lose privatehood of A::B, but if you think about it, that makes sense, or you wouldn't be able to implement foo::bar.

spraff
  • 32,570
  • 22
  • 121
  • 229
  • Doesn't explain why this works with global-namespace functions. – Puppy Oct 11 '11 at 15:40
  • 1
    It works with global functions because the friend is also being overloaded as a function declaration. When it's namespace qualified it doesn't know what the namespace-nested thing refers to. – Mark B Oct 11 '11 at 15:43
  • Unfortunately keeping A::B private is a critical part of the class design. – Jay Walker Oct 12 '11 at 17:02
0

It doesn't look like you can do that. What you can do is forward declare a class with a static member, and the class gets friendship. But any time you use friendship at least take one more look at your design and ask yourself why. It may be fine or there may be a better answer.

namespace foo { class bar; }

class A
{
   class B
   {
   };

   friend class ::foo::bar;
};

namespace foo
{
   class bar
   {
   public:
      static int run_bar( A::B& )
      {
      }
   };
}
Mark B
  • 95,107
  • 10
  • 109
  • 188
0

You can't do it because there is no way to write a forward declaration of A::B and hence forward declare foo(A::B&).

However, you can use a trick known as "friend name injection" to declare (and possibly define) a friend function in the class definition. This way you can call that friend function without namespace qualifiers (e.g. bar(b) instead of foo::bar(b)) and argument-dependent lookup will successfully resolve the call:

struct A
{
    struct B {};

    friend int bar(B&) { // inject bar() into the outer namespace
        // implementation here
    }
};

namespace foo
{
   int foo()
   {
       A::B b;
       return bar(b); // it calls bar(B&) declared in A
   }
}
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271