嵌套类的同班朋友可以访问外部类成员吗?

时间:2019-04-04 14:10:21

标签: c++ language-lawyer c++17 inner-classes access-control

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实际上有效吗?

我发现的相关标准件是:

[class.access.nest]

  

嵌套类是成员,因此具有与任何其他成员相同的访问权限。

尽管这并不意味着嵌套类的朋友拥有与嵌套类相同的权限。

[class.access.base]/5

  

对成员的访问受该成员所在的类影响。该命名类是在其中查找并找到成员名称的类。如果在类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

1 个答案:

答案 0 :(得分:6)

我认为gcc和msvc是正确的。

[class.friend]/1中,重点是我:

  

一个类的朋友是一个功能或类,被授予使用该类的私有名称和受保护成员名称的权限。类通过朋友声明来指定其朋友(如果有)。这样的声明为朋友提供了特殊的访问权,但它们并未使被提名的朋友成为朋友班的成员。

拥有friend E f1()时,将授予f1使用B 的私有名称和受保护名称​​的权限。 E不是B的私有名称或受保护的名称,它是B也可以访问的名称。


从概念上讲,它类似于[class.friend]/10

  

友谊既不是继承也不是传递。

因为这意味着规则是f1可以访问B的东西,而B可以访问A的东西,因此{{1} }可以访问f1的内容。