What is the fully qualified name of a friend funct

2020-02-08 07:26发布

问题:

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.

回答1:

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).



回答2:

C++ Standard [7.3.1.2/3 (of ISO/IEC 14882:2011)]:

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. The name of the friend is not found by unqualified lookup (3.4.1) or by qualified lookup (3.4.3) until a matching declaration is provided in that namespace scope (either before or after the class definition granting friendship). If a friend function is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2). If the name in a friend declaration is neither qualified nor a template-id and the declaration is a function or an elaborated-type-specifier, the lookup to determine whether the entity has been previously declared shall not consider any scopes outside the innermost enclosing namespace.