Firstly, the friend declaration itself doesn't make f
visible for name lookup, f
could only be found by ADL.
A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not visible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided
From the standard, [namespace.memdef]/3,
The friend declaration does not by itself make the name visible to unqualified lookup or qualified lookup. [ Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship). — end note ] If a friend function or function template 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 ([basic.lookup.argdep]). 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.
The problem is for ADL to be applied, whether f(a)
is a function call must be determined in advance.
[basic.lookup.unqual]/3,
(emphasis mine)
The lookup for an unqualified name used as the postfix-expression of a function call is described in [basic.lookup.argdep]. [ Note: For purposes of determining (during parsing) whether an expression is a postfix-expression for a function call, the usual name lookup rules apply.
In this stage, the function name f
is invisible and the typename f
is found, then f(a)
is considered not to be a function all, then ADL won't be applied at all.
Because the expression is not a function call, the argument-dependent name lookup ([basic.lookup.argdep]) does not apply and the friend function f is not found.
BTW: Adding the declaration of f
at namespace scope makes the function name f
visible and f(a)
would be considered as function call (and then you'll get the error that f
returns void
which can't be used to initialize i
). e.g.
typedef int f;
namespace N {
struct A;
void f(A &);
struct A {
friend void f(A &);
operator int();
void g(A a) {
int i = f(a); // f is the friend function now
}
};
}