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.
Friend functions can be defined (given a function body) inside class declarations. These functions are inline functions. Like member inline functions, they behave as though they were defined immediately after all class members have been seen, but before the class scope is closed (at the end of the class declaration).
A friend function is declared inside the class with a friend keyword preceding as shown below. class className{ …… friend returnType functionName(arg list); }; As shown above, the friend function is declared inside the class whose private and protected data members are to be accessed.
A friend function of a class is defined outside that class' scope but it has the right to access all private and protected members of the class. Even though the prototypes for friend functions appear in the class definition, friends are not member functions.
Explanation: Friend function can be declared either in private or public part of the class.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With