Search code examples
c++namespacesargument-dependent-lookup

Class unable to make friends with a function that's not in its namespace


I'm having difficulty understanding why the following MWE does not compile:

#include <iostream>

namespace N
{
    class Foo
    {
        friend void bar( Foo& f );
        void print(){ std::cout << "..." << std::endl; }    // private by default
    };  
}

void bar( N::Foo& f )
{
    f.print();
}

int main()
{
}

g++ 4.8.2 error

Test.cpp: In function ‘void bar(N::Foo&)’:
Test.cpp:8:8: error: ‘void N::Foo::print()’ is private
   void print(){ std::cout << "..." << std::endl; } // private by default
        ^
Test.cpp:14:10: error: within this context

I'm almost certainly missing something here but surely the friend function bar() can access any private member of the class N::Foo.

Note that:

  1. moving bar() into the namespace N resolves this error.
  2. the code compiles if ::bar() does not call N::Foo::print()

Why doesn't the code compile as is?

Edit

On second thoughts the title of this question does not precisely describe the problem. I'll edit it in due course.


Solution

  • An unqualified friend declaration refers to a function in the namespace containing the class, introducing that function into the namespace if it hasn't already been declared.

    I'd move the function into the namespace. It can be found by argument-dependent lookup, so you can call it with an unqualified bar(foo) without the need for N::.

    If you do want it in the global namespace for some reason, then you'll need to declare it in the global namespace before you can declare it a friend. This is a bit messy:

    // Need the class definition to declare the function
    namespace N {class Foo;}
    
    // Need the function definition to declare it a friend
    void bar( N::Foo& );
    
    // Need the class definition to define the function
    namespace N
    {
        class Foo
        {
            friend void ::bar( Foo& f );
            void print(){ std::cout << "..." << std::endl; }    // private by default
        };  
    }
    
    // And finally the function
    void bar( N::Foo& f )
    {
        f.print();
    }