3
votes

If I have a function defined as friend inside a class. What is the namespace of that function?

namespace A{namespace B{
    struct S{
        friend void f(S const&){};
    };
}}

int main(){
    A::B::S s{};
    f(s); // ok
    ::f(s); // not ok, no f in global namespace
    A::B::f(s); // no f in A::B
    A::B::S::f(s); // not ok, no f in A::B::S
}

Does it even have a namespace? Does it make sense for it to have a namespace?

What if I want to disambiguate a call in the commonly used idiom?

using std::swap;
swap(a, b);

Am I forced to define it outside the class and declare it a friend?

3
you can always declare the function in some namespace before befriending it.Walter
The plain expression f(s) works because of argument-dependent lookup. The function is declared in the scope of A::B::S, but it's not a member of the class.Some programmer dude
@Someprogrammerdude doesn't this (ADL) suggest that f is in A::B (same namespace as its argument)?Walter
disambiguation is not necessary because of ADL. That's the whole point of that idiom: in order for std::swap to be considered in the lookup (for types other than those in std) you must bring it into the local scope.Walter
@Some programmer dude: The function is actually in A::B, not in A::B::S. It is just not visible to ordinary name lookup.AnT

3 Answers

6
votes

A friend declaration refers to a function from the narrowest enclosing namespace. However, it does not introduce the name of that function into the namespace

9.3.1.2 Namespace member definitions
3 If a friend declaration in a non-local class first declares a class, function, class template or function template the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup or qualified lookup.

http://eel.is/c++draft/namespace.memdef#3

So, in your case A::B::f is a friend. However, name f does not exist in A::B yet. You will not be able to refer to it as A::B::f until you provide an explicit declaration of f in A::B.

Name lookup is able to find f in unqualified f(s) call through a special mechanism called Argument Dependent Lookup (ADL). ADL is the only mechanism that can "see" your function.

3
votes

The function is in A::B but it can only be found via ADL or Koenig Lookup.

The only way for it to be named in main is by introducing it outside the class definition.

Yes this is strange.

namespace A{namespace B{
  inline void f(S const&);
  struct S{
    friend void f(S const&){};
  };
}}

Now A::B::f names f.

This technique is something I use to introduce operators and other customization points. It also permits creating per-template instance functions on template classes that are not themselves templates.

template<class T>
struct foo {
  friend void not_a_template( T ){}
};
template<class T>
void is_a_template(T){}
1
votes

Yes, you define function outside and make it a friend to a certain class. It's absolutely independent function, but you allow it to acces certain class.

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.