我正在阅读此blog post section,我尝试使用提供的代码段。
namespace N {
// 2
class A {
friend void f(A) {} // 1
};
}
如果我理解正确,// 1
中的定义会在f
所在的位置注入名称// 2
。
但是它只能通过参数依赖查找来获得。细
帖子中有一句引起我注意的句子:
7.3.1.2/3命名空间成员定义[namespace.memdef] p3
首先在名称空间中声明的每个名称都是该名称空间的成员。如果非本地类中的友元声明首先声明了类,函数,类模板或函数模板,则该友元是最内部封闭命名空间的成员。友元声明本身不会使名称对非限定查找(3.4.1)或限定查找(3.4.3)可见。
请注意,没有任何地方声明友元声明引入的名称必须与声明和/或定义的类的名称有任何特定的关系,或者与该类的任何特定关系(就此而言) )。
据此,我认为以下代码段有效:
namespace N {
struct A {
};
struct B {
friend void f(A) {
}
};
int main() {
N::A a;
f(a);
}
但它被GCC7和Clang 4都拒绝了。
t.cpp:19:3:错误:'f'未在此范围内声明
有趣的是,当我尝试使用f
对象调用N::B
时,我收到以下错误:
t.cpp:12:6:错误:无法将'B'从'N :: B'转换为'N :: A'
所以这是我的问题:
不应该通过ADL检测到f(A)
吗?由于这两个类都在命名空间中,我不明白为什么会失败。我在标准中查看了关于朋友的部分,但未找到相关部分。
我想知道注入了哪个范围f(A)
,因为当我尝试通过调用f(B)
来提供错误的参数类型时,GCC能够找到它。
答案 0 :(得分:2)
来自cppreference/cpp/language/friend
:
在类或类模板
X
中的友元声明中首先声明的名称将成为X
最内层封闭命名空间的成员,但无法进行查找(除了依赖于参数的查找)X
)除非提供了命名空间范围内的匹配声明 - 有关详细信息,请参阅namespaces。
来自cppreference/cpp/language/namespace
:
非本地类
X
中的友元声明引入的名称成为X
最内层封闭命名空间的成员,但它们不会对查找可见(既不合格也不合格),除非匹配声明在命名空间范围内提供,在类定义之前或之后。可以通过ADL找到这样的名称,ADL同时考虑名称空间和类。
这与您的示例一致 - f
采用A
,这与封闭类的类型不同。
如果您将示例更改为...
namespace N {
struct A {
};
struct B {
friend void f(B) {
}
};
int main() {
N::B b;
f(b);
}
......它会编译。
相关标准报价:
类的朋友是一个函数或类,它被授予使用类中的私有和受保护成员名称的权限。 [...]当且仅当该类是非本地类([class.local]),函数名称是非限定的,并且该函数具有命名空间范围时,才能在类的友元声明中定义函数。 [...]这样的函数隐含地是内联函数。 在类中定义的友元函数位于定义它的类的(词法)范围内。在类外定义的友元函数不是([basic.lookup.unqual])。< / p>