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