嵌套类继承出现错误

25
class A {};
class B : private A {};
class C : private B
{
public:
    class D : private A {}; // Error here
};

这段代码在VS 2013中出现如下错误:

nested.cpp(8) : error C2247: 'A' not accessible because 'B' uses 'private' to inherit from 'A'

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

class D : private ::A {};

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

起初我认为这是因为C私有继承了B导致基类被隐藏。但是,如果我消除中间类B,仅使用以下代码:

class A {};
class C : private A
{
public:
    class D : private A {};
};

错误消失了。


1
编译器告诉你为什么。这是因为“B”使用“private”从“A”继承,而不是因为C私有地继承自B。 - n. m.
@n.m. 嗯,这并没有告诉我太多信息。毕竟,我正在使用全局命名空间中的一个类,而不是某个成员。无论如何,现在我明白了,感谢答案。 - user1610015
名称从内向外搜索。当找到一个名称时,搜索停止。然后检查可访问性。在您的情况下,搜索路径为C>B>A。B>A由于私有继承而被阻止,A是不可访问的。这就是编译器告诉你的。全局范围中也存在A,并且它是相同的A,这是无关紧要的。 - n. m.
1
@n.m. 不,编译器没有告诉我所有的那些... - user1610015
5个回答

18

引自cppreference

根据未限定名称查找为私有的名称,在通过限定名称查找时可能是可访问的。

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

class A {};

class B : private A {};

class C : private B
{
public:
    class D : private A {}; // Error here
};
  1. AC 的作用域中查找。如果在那里定义了它,就不会有问题。
  2. 它发现 A 是由它的基类(私有)B 私有继承而来,因此抛出编译器错误。 Clang 说:
    note: constrained by private inheritance here:
    class B : private A {}; 

同样,根据引语所述,如果使用完全限定名称,就应该可以工作,就像您展示的那样。

class D : private ::A {};

至于你提到的最后一个例子:

class A {};

class C : private A
{
public:
    class D : private A {};
};

这种方法有效是因为,名称查找对于所有同一类的名称都有效。再次引用cppreference的话:

类的所有成员(成员函数的函数体、成员对象的初始化程序以及整个嵌套类定义)都可以访问类可以访问的所有名称。


它可能会自动推断出答案吗?一个答案应该是一个答案,而不是另一个潜在的问题。 - skypjack

12

在名称查找期间,它涉及到作用域的问题:

  • 当您使用::A时,这是一个完全限定的名称,因此您明确地引用全局命名空间并从中选择A

  • 当您从A继承时,C(让我这么说)看到A,并且您可以在C中直接使用未限定的名称引用A名称。

  • 当您从B继承时,C“看到”B,而A在其范围内是私有的。它是私有的,但它确实存在。因为A是一个未限定的名称,并且首先在该作用域中进行查找,所以它被发现了但无法访问,因此会出现错误。


7

以下是来自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 ++不会从您无法访问它们的作用域中“隐藏”私有名称,而只使用访问说明符作为使用名称的“许可”。此外,这些权限绑定到名称,而不是命名实体(在本例中为类)。
因此,在您的代码中,在未经限定的查找中,编译器找到名称C :: B :: A :: A ,该名称隐藏了名称 ::A ,然后检查访问权限,并发现此名称在当前上下文中是私有的,因为它是B 的私有基类中C :: B :: A 的作用域中的名称,不能从C 内部访问。

2

class D : private ::A {};

当您从B继承时,C会看到B并且A在其作用域中是私有的。尽管A是私有的,但它仍然存在。因为A是一个未限定的名称并且首先在该作用域中寻找,所以它被找到但无法访问,因此出现错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接