Why a friend function is not treated as a member of a namespace of a class it was declared in without additional declaration?

  c++, friend, header-files, namespaces

Suppose we have a class foo from a namespace space which declares a friend function named bar, which is later on defined, like so:

namespace space {
    struct foo {
        friend void bar(foo);
    };
}

namespace space {
    void bar(foo f) { std::cout << "friend from a namespacen"; }
}

To my understanding, friend void bar(foo); declares bar to be a free function inside space taking a foo by value. To use it, we can simply do:

auto f = space::foo();
bar(f);

My understanding is that we don’t have to say space::bar, because ADL will see that bar is defined in the same namespace as foo (its argument) and allow us to omit the full name qualification. Nonetheless, we are permitted to qualify it:

auto f = space::foo();
space::bar(f);

which works (and should work) exactly the same.

Things started to get weird when I introduced other files. Suppose that we move the class and the declaration to foo.hpp:

#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP

namespace space {
    struct foo {
        friend void bar(foo);
    };
}

#endif //PROJECT_FOO_HPP

and the definitions to foo.cpp:

#include "foo.hpp"
#include <iostream>

namespace space {
    void bar(foo f) { std::cout << "friend from a namespacen"; }
}

notice that all I did was I moved (didn’t change any code) stuff to a .hpp.cpp pair.

What happened then? Well, assuming that we #include "foo.hpp", we still can do:

auto f = space::foo();
bar(f);

but, we are no longer able to do:

auto f = space::foo();
space::bar(f);

This fails saying that: error: 'bar' is not a member of 'space', which is, well, confusing. I am fairly certain that bar is a member of space, unless I misunderstood something heavily. What’s also interesting is the fact that if we additionaly declare (again!) bar, but outisde of foo, it works. What I mean by that is if we change foo.hpp to this:

#ifndef PROJECT_FOO_HPP
#define PROJECT_FOO_HPP

namespace space {
    struct foo {
        friend void bar(foo);
    };
    
    void bar(foo); // the only change!
}

#endif //PROJECT_FOO_HPP

it now works.

Is there something with header / implementation files that alters the expected (at least for me) behaviour? Why is that? Is this a bug? I am using gcc version 10.2.0 (Rev9, Built by MSYS2 project).

Source: Windows Questions C++

LEAVE A COMMENT