clang ++,g ++和MSVC disagree on this code:
class A {
private:
enum class E { NO, YES };
class B {
private:
friend E f1() { return E::YES; }
// friend E f2();
};
};
// A::E f2() { return A::E::YES; }
int main() {}
clang ++接受如下所示的代码。 g ++和MSVC在f1
中抱怨A::E
无法访问。如果未对函数f2
进行注释,则所有三个编译器都会对其定义抱怨A::E
无法访问。
f1
实际上有效吗?
我发现的相关标准件是:
嵌套类是成员,因此具有与任何其他成员相同的访问权限。
尽管这并不意味着嵌套类的朋友拥有与嵌套类相同的权限。
对成员的访问受该成员所在的类影响。该命名类是在其中查找并找到成员名称的类。如果在类
m
中命名,则成员N
在点 R 上可访问,如果
- 作为
m
作为N
的成员是公开的,或者m
成员的- 作为
N
是私有的, R 发生在N
类的成员或朋友中,或者m
成员的
N
受保护,并且...或存在
B
的基类N
,可以在 R 访问,m
可以在 R 在类B
中命名。
因此f2
是无效的,因为A::E
的命名类肯定是A
,不涉及基类,并且f2
的定义不是A
的成员或朋友,并且没有“出现” A
的成员或朋友。
在f1
中,不合格的E
的命名类别也是A
。 ([[basic.lookup.unqual]说,首先完成了在类{{1}中对名称E
的查找,但是在那里没有“找到”,因此可以在类A::B
中进行查找完成,然后找到该成员。)但是我想最大的问题是,A
的定义是否“出现”在f1
的成员中?如果是这样,则该成员必须为A
。
答案 0 :(得分:6)
我认为gcc和msvc是正确的。
在[class.friend]/1中,重点是我:
一个类的朋友是一个功能或类,被授予使用该类的私有名称和受保护成员名称的权限。类通过朋友声明来指定其朋友(如果有)。这样的声明为朋友提供了特殊的访问权,但它们并未使被提名的朋友成为朋友班的成员。
拥有friend E f1()
时,将授予f1
使用B
的私有名称和受保护名称的权限。 E
不是B
的私有名称或受保护的名称,它是B
也可以访问的名称。
从概念上讲,它类似于[class.friend]/10:
友谊既不是继承也不是传递。
因为这意味着规则是f1
可以访问B
的东西,而B
可以访问A
的东西,因此{{1} }可以访问f1
的内容。