关于使用友元函数查找名字的问题

A question about name lookup with friend function

我已经阅读了 [basic.lookup.unqual] 的标准部分,对此我感到困惑:

typedef int f;
namespace N {
  struct A {
    friend void f(A &);
    operator int();
    void g(A a) {
      int i = f(a);  // f is the typedef, not the friend function: equivalent to int(a)
    }
  };
}

请考虑以上代码;我不明白为什么名称 f 的类型是 int,而不是 void f(A &)。根据我的理解,名称查找应该首先在 class 范围 A 中找到 void f(A &)。如果在那里找不到名称,它将在外部命名空间中执行查找。显然,在 class A 中有一个名称 void f(A &) 并且正如标准所说:

name lookup ends as soon as a declaration is found for the name

那么,如果还有其他关于这些的特定规则,为什么名称在这里指的是类型 int

首先,friend declaration 本身不会使 f 对名称查找可见,f 只能通过 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

从标准,[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.

问题是要应用ADL,需要提前判断f(a)是否是函数调用

[basic.lookup.unqual]/3,

(强调我的)

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.

在这个阶段,函数名f是不可见的,而typenamef是找到的,那么f(a)就认为不是函数all,那么ADL就不会完全被应用。

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.

顺便说一句:在命名空间范围内添加 f 的声明会使函数名称 f 可见,并且 f(a) 将被视为函数调用(然后你会得到错误f returns 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
    }
  };
}