嵌套类继承出错

时间:2016-12-09 07:17:49

标签: c++ inheritance visual-studio-2013

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'使用'private'继承'A'

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

class D : private ::A {};

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

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

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

错误消失了。

5 个答案:

答案 0 :(得分:18)

引自 cppreference

  

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

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

class A {};

class B : private A {};

class C : private B
{
public:
    class D : private A {}; // Error here
};
    A范围内查找
  1. C。如果在那里定义,就没有问题。
  2. 它指出A是由它的基础(私有)类B私有继承的,因此会引发编译错误。 Clang说:
    note: constrained by private inheritance here:
    class B : private A {}; 
  3. 同样,根据引用它应该有效,如果你使用完全限定名称,就像你已经显示

    class D : private ::A {};
    

    至于你的上一个例子:

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

    它起作用,因为名称查找适用于属于同一个类的所有名称。再次引用cppreference:

      

    一个类的所有成员(成员函数体,初始化函数)   成员对象和整个嵌套类定义)具有访问权限   一个类可以访问的所有名称。

答案 1 :(得分:13)

这是名称查找过程中的范围问题:

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

  • 当您从A继承时,C(让我说)看到A,您可以直接引用A中的C名称用不合格的名字。

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

答案 2 :(得分: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
};

使用私有继承,基类的public和protected成员将成为派生类的私有成员。

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的公共和受保护成员。

答案 3 :(得分:4)

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

  

将一个类的名称注入到类中。范围。

也就是说,如果你有

class A {};

然后,您不仅可以通过名称A来引用课程::A,还可以使用名称A::A来引用课程A。请注意,尽管描述了相同的类,但它们不是的名称相同,因为它们位于不同的范围内。

现在,当A范围内或直接或间接来自A::A的类时,非限定查询会找到::A而不是A::A(除非A本身隐藏了另一个名字。)

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

因此,在您的代码中,在C::B::A::A的非限定查找中,编译器会找到隐藏名称::A的名称C::B::A,然后检查访问权限并发现此名称是私有的当前上下文,因为它是C范围内的名称,无法从A内访问,因为Bcreate table Hotel(id_Hotel INT, id_Manager INT...) 的私有基类。

答案 4 :(得分:2)

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