Во-первых, само объявление само по себе не делает f
видимым для поиска по имени, f
может быть найдено только ADL.
Имя, впервые объявленное в объявлении друга в пределах класса или шаблона класса X становится членом внутреннего вложенного пространства имен X, но не отображается для поиска (кроме поиска, зависящего от аргумента, который учитывает X), если не предоставлено соответствующее объявление в области пространства имен
Из стандарта [namespace.memdef] / 3 ,
Объявление друга само по себе не делает имя видимым для неквалифицированного или квалифицированного поиска , [Примечание: имя друга будет отображаться в его пространстве имен, если в области имен пространства предоставляется соответствующее объявление (либо до, либо после определения класса, предоставляющего дружбу). - примечание конца] Если вызывается функция или шаблон функции друга, ее имя может быть найдено с помощью поиска имен, который рассматривает функции из пространств имен и классов, связанных с типами аргументов функции ([basi c .lookup.argdep]) , Если имя в объявлении друга не является ни квалифицированным, ни идентификатором шаблона, и объявление является функцией или разработанным спецификатором типа, поиск, чтобы определить, был ли объект ранее объявлен, не должен рассматривать какие-либо области за пределами самого внутреннего окружающего пространства имен .
Проблема заключается в том, что для применения ADL необходимо заранее определить, является ли f(a)
вызовом функции.
[basi c .lookup. unqual] / 3 ,
(выделено мной)
Поиск неквалифицированного имени, используемого в качестве постфиксного выражения для вызова функции, описан в [basi c .lookup.argdep]. [Примечание: В целях определения (во время синтаксического анализа), является ли выражение постфиксным выражением для вызова функции, применяются обычные правила поиска имен .
На этом этапе имя функции f
невидимо и найдено имя типа f
, тогда f(a)
считается не функцией всех, тогда ADL вообще не будет применяться.
Поскольку выражение не является вызовом функции, поиск имени в зависимости от аргумента ([basi c .lookup.argdep]) не применяется, и функция f друга не найдена.
КСТАТИ: Добавление объявление f
в области имен делает имя функции f
видимым, а f(a)
будет рассматриваться как вызов функции (и тогда вы получите ошибку, что f
возвращает void
, которая не может быть использована инициализировать i
). например,
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
}
};
}