嵌套类继承错误

Posted

技术标签:

【中文标题】嵌套类继承错误【英文标题】:Error with nested class inheritance 【发布时间】:2016-12-09 07:17:49 【问题描述】:
class A ;
class B : private A ;
class C : private B

public:
    class D : private A ; // Error here
;

此代码给出以下错误(在 VS 2013 中):

nested.cpp(8):错误 C2247:“A”不可访问,因为“B”使用“私有”从“A”继承

如果我像这样更改D 的定义,它会得到修复:

class D : private ::A ;

这是正确的行为吗?如果是,为什么?

起初我以为是因为C 私下继承自B 会隐藏基类。但如果我消除“中间人”类B 并使用它:

class A ;
class C : private A

public:
    class D : private A ;
;

错误消失了。

【问题讨论】:

编译器告诉你原因。 因为'B'使用'private'继承自'A',而不是因为C私有地继承自B。 @n.m.好吧,这并没有告诉我太多。毕竟,我使用的是全局命名空间中的一个类,而不是某个东西的成员。无论如何,感谢答案,我现在明白了。 名称是从内向外搜索的。找到名称后,搜索停止。然后检查可访问性。在您的情况下,搜索路径是 C>B>A。 B>A 因为私有继承而被阻塞,A 不可访问。这是编译器告诉你的。 A 也存在于全局范围内,并且是同一个 A,是无关紧要的。 @n.m.不,编译器并没有告诉我所有这些...... 【参考方案1】:

引用自cppreference

根据非限定名称查找的私有名称,可能是 可通过限定名称查找访问

考虑到这一点,让我们看看第一个示例的非限定名称查找如何工作:

class A ;

class B : private A ;

class C : private B

public:
    class D : private A ; // Error here
;
    C 的范围内查找A。如果在那里定义,就不会有问题。 它发现A 是由它的基(私有)类B 私有继承的,因此会引发编译器错误。 Clang 说:
    注意:此处受私有继承的约束:
    B类:私人A ; 

再次,根据报价,如果您使用完全限定名,就像您展示的那样,它应该可以工作

class D : private ::A ;

至于你的最后一个例子:

class A ;

class C : private A

public:
    class D : private A ;
;

之所以有效,是因为名称查找适用于属于同一类的所有名称。再次引用 cppreference:

类的所有成员(成员函数的主体, 成员对象和整个嵌套类定义)可以访问 类可以访问的所有名称。

【讨论】:

大概算出来了?答案应该是一个答案,而不是另一个潜在的问题。【参考方案2】:

这是名称查找期间的范围问题:

当您使用 ::A 时,它是一个完全限定名称,因此您明确引用全局命名空间并从那里选择 A

当您从A 继承时,C(让我说)看到A,您可以直接在C 中使用非限定名称引用A 名称。

当您从 B 继承时,C 看到 BA 在其范围内是私有的。它是私人的,但它存在。因为A 是一个非限定名称,并且首先在该范围内查找,所以它恰好被发现且无法访问,因此出现错误。

【讨论】:

【参考方案3】:

来自cppreference的示例:

class A  ;
class B : private A  ;
class C : public B 
   A* p; // error: unqualified name lookup finds A as the private base of B
   ::A* q; // OK, qualified name lookup finds the namespace-level declaration
;

通过私有继承,基类的公共和受保护成员成为派生类的私有成员。

class B : private A ;

class C : private B

public:
    class D : private A ; // Error because all members of A is private to B so what 
    //would be the use of this private inheritance if you can't access any of A's member.
;

虽然

class D :private ::A ; 

之所以有效,是因为 A 的成员直接取自全局命名空间,使 D 能够访问 A 的公共和受保护成员。

【讨论】:

【参考方案4】:

其他答案中没有明确说明的理解的关键部分如下:

类的名称被注入到类的作用域中。

也就是说,如果你有

class A ;

那么您不仅可以通过名称::A 还可以通过名称A::A 来引用类A。请注意,尽管描述的是同一个类,但它们同名,因为它们在不同的范围内。

现在,当在 A 或直接或间接派生自 A 的类的范围内时,不合格的查找将找到 A::A 而不是 ::A(除非 A::A 本身被另一个名称隐藏)。

此外,与其他一些语言不同,C++ 不会隐藏您无法访问它们的作用域中的私有名称,而是仅将访问说明符用作使用名称的权限 .此外,这些权限绑定到名称,而不是命名实体(在本例中为类)。

因此,在您的代码中,在对 A 进行非限定查找时,编译器会找到名称 C::B::A::A,它隐藏了名称 ::A,然后检查访问权限并发现该名称在当前上下文中是私有的,因为它是C::B::A 范围内的名称,不能从C 中访问,因为AB 的私有基类。

【讨论】:

【参考方案5】:

类 D : 私有 ::A ; 当您从 B 继承时,C 看到 B 并且 A 在其范围内是私有的。它是私人的,但它存在。因为 A 是一个非限定名称,并且首先在该范围内查找它,所以它恰好被发现且无法访问,因此出现错误。

【讨论】:

以上是关于嵌套类继承错误的主要内容,如果未能解决你的问题,请参考以下文章

你能在 C++ 中保护嵌套类吗?

C# 嵌套类和继承

继承和嵌套类

为什么嵌套类“继承”?

如何覆盖继承的嵌套类中存在的虚拟方法

具有覆盖函数的嵌套类的编译器错误 - Swift Xcode6