c++namespacesfriendname-lookupfully-qualified-naming

What is the fully qualified name of a friend function defined inside of a class?


What is the fully qualified name of a friend function defined inside of a class?

I recently saw an example analogous to the following. What is the fully qualified name of val() below?

#include <iostream>

namespace foo {
    class A {
        int x;
    public:
        A(int x = 0) : x(x) { }

        friend int val(const A &a) { return a.x; }
    };
}

int main() {
    foo::A a(42);

    // val() found using ADL:
    std::cout << val(a) << std::endl;

    // foo::val(a); // error: 'val' is not a member of 'foo'
    // foo::A::val(a); // error: 'val' is not a member of 'foo::A'

    return 0;   
}

Is argument-dependent lookup the only way val() can be found?

Admittedly, this does not stem from a practical problem. I am simply looking to gain a better understanding.


Solution

  • Is argument-dependent lookup the only way val() can be found?

    Yes, it is the only way. To quote the holy standard at [namespace.memdef]/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.

    So while val is a member of foo, it's not visible to lookup from the friend declaration alone. An out of class definition (which is also a declaration) is required to make it visible. For an inline definition (and no out-of-class declaration) it means ADL is the only way to call the function.


    As an added bonus, C++ did once have a concept of "friend name injection". That however has been removed, and the rules for ADL adjusted as a replacement. A more detailed overview can be found in WG21 paper N0777 (pdf).