4

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?

alfC
  • 14,261
  • 4
  • 67
  • 118
  • you can always declare the function in some namespace before befriending it. – Walter Jul 19 '18 at 22:40
  • The plain expression `f(s)` works because of [*argument-dependent lookup*](https://en.cppreference.com/w/cpp/language/adl). The function is declared in the scope of `A::B::S`, but it's not a member of the class. – Some programmer dude Jul 19 '18 at 22:40
  • @Someprogrammerdude doesn't this (ADL) suggest that `f` is in `A::B` (same namespace as its argument)? – Walter Jul 19 '18 at 22:41
  • 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 Jul 19 '18 at 22:52
  • 1
    @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 stands with Russia Jul 19 '18 at 23:16

3 Answers3

6

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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
4

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){}
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    This is interesting, it means that if I define a function like this, it will have to have at least one argument that is of the enclosing type. Otherwise it will never be found outside the namespace. Stranger yet. – alfC Jul 20 '18 at 00:59
1

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.

PStarczewski
  • 479
  • 2
  • 17
  • @Walter It *is* a member of `A::B`, but since there is no declaration of `f` outside the lexical scope of `A::B::S`, `f` can only be called through unqualified lookup (and found through ADL). – David G Jul 19 '18 at 22:54
  • 1
    @0x499602D2 that sounds like an answer to me. Please post it. – Walter Jul 19 '18 at 22:55
  • I believe that @0x – PStarczewski Jul 19 '18 at 22:58
  • I believe that @0x499602D2 try to say what i tried but in more sensible way. I'm not native english speaker so forgive me my mistakes. C++ have block lexical scopes, so you have to define it there. When you declare an unqualified friend function, it names a function in the nearest namespace scope. In a situation when friend function wasn't declared previously, declaration isn't visible in normal lookup. Sorry, I'm new, cant add comments properly yet. – PStarczewski Jul 19 '18 at 23:04
  • If anything is not understandable please let me know, I'll try to explain my statements and way of thinking better. – PStarczewski Jul 19 '18 at 23:09