How can I declare a friend function in a namespace that takes an inner class as a parameter?
Asked Answered
F

5

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?

Furiya answered 11/10, 2011 at 15:20 Comment(3)
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.Retsina
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)?Bowens
Related: Passing anonymous classes to private member functionsLeeann
B
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_);
}
Belfast answered 11/10, 2011 at 15:44 Comment(1)
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.Furiya
E
0

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

Elkeelkhound answered 11/10, 2011 at 15:34 Comment(3)
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.Furiya
VC10 still complains 'bar' : is not a member of 'foo'Disconcert
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.Belfast
H
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.

Hocus answered 11/10, 2011 at 15:38 Comment(3)
Doesn't explain why this works with global-namespace functions.Elkeelkhound
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.Military
Unfortunately keeping A::B private is a critical part of the class design.Furiya
M
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& )
      {
      }
   };
}
Military answered 11/10, 2011 at 15:44 Comment(0)
T
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
   }
}
Testa answered 11/10, 2011 at 17:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.