我已经阅读了[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 &)
。以我的理解,名称查找应该首先在类范围A中找到void f(A &)
。如果在那里找不到名称,它将在外部名称空间中执行查找。显然,在类void f(A &)
中有一个名称A
,正如标准所说:
找到名称的声明后,名称查找立即结束
那么,如果还有其他关于这些的特殊规则,那么为什么名称在这里引用类型int
?
首先,friend declaration本身不会使f
对于名称查找可见,f
只能由ADL找到。
首先在类或类模板X的朋友声明中声明的名称成为X的最内层命名空间的成员,但对于查找(对于考虑X的依赖于参数的查找除外)不可见,除非在命名空间中有匹配的声明提供范围
根据标准,[namespace.memdef]/3,
朋友声明本身不会使名称对不合格的查询或合格的查询可见。 [注意:如果在名称空间范围内(在授予友谊的类定义之前或之后)提供了匹配的声明,则朋友的名称将在其名称空间中可见。 —注释[end note]如果调用了朋友函数或函数模板,则可以通过名称查找来找到其名称,该名称查找考虑了来自与函数参数类型([basic.lookup.argdep])相关的名称空间和类中的函数。如果朋友声明中的名称既不是限定名称也不是模板ID,并且该声明是函数或精化类型说明符,则用于确定该实体先前是否已声明的查找将不考虑最内部封闭的命名空间之外的任何范围。 。
问题是要应用ADL,必须事先确定f(a)
是否为函数调用。
(重点是我的)
在[basic.lookup.argdep]中描述了对用作函数调用的后缀表达式的不合格名称的查找。 [注意:为了确定(在解析过程中)表达式是否为函数调用的后缀表达式,通常的名称查找规则适用。>]
在此阶段,功能名称
f
是不可见的,并且找到类型名称f
,然后将f(a)
视为不是全部功能,则根本不会应用ADL。
因为表达式不是函数调用,所以不应用依赖于参数的名称查找([basic.lookup.argdep],并且找不到朋友函数f。
BTW:在命名空间范围内添加
f
的声明使函数名称f
可见,并且f(a)
将被视为函数调用。例如
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
}
};
}