C++ 私有基类与多重继承
考虑:C++ 私有基类与多重继承,c++,C++,考虑: struct A { int x;}; struct B : A {}; struct C : private A {}; 现在,正如预期的那样,代码 struct D : C { D () { C::x = 2; } }; int main () { D d; } 不编译: test2.cc: In constructor ‘D::D()’: test2.cc:1:16: error: ‘int A::x’ is inaccessible test2.cc:7:12: e
struct A { int x;};
struct B : A {};
struct C : private A {};
现在,正如预期的那样,代码
struct D : C
{
D () { C::x = 2; }
};
int main () { D d; }
不编译:
test2.cc: In constructor ‘D::D()’:
test2.cc:1:16: error: ‘int A::x’ is inaccessible
test2.cc:7:12: error: within this context
现在,如果我这样做了
struct D : B, C
{
D () { C::x = 2; }
};
int main () { D d; }
然后错误消失了!A::x
不也应该是不可访问的吗?这里的解释是什么
我使用的是gcc版本4.7.2(gcc)
,linux x86_64,如果这很重要的话
编辑:它不使用Clang 3.2进行编译:
但GCC4.7.2确实如此:这肯定是一个错误。从类
B
继承也没有理由改变C
成员的可访问性
甚至GCC4.8.0(测试版)似乎都没有解决这个问题。另一方面,Clang 3.2和ICC 13.0.1。我不确定确切原因,但我知道: 在菱形模式中使用多重继承时,派生对象D中会有基类a的多个副本 在您的例子中,对象D有两个名为A::x的成员,这可能会导致编译器的混淆
这被称为答案是,叮当声是正确的。然而,根据标准,该代码也可能因不明确而失败 如果你看11.2p5,它有一个相关的注释(是的,我知道注释是非规范性的): [注:此类可以是显式的,例如,当使用限定id时, 或隐式,例如,当使用类成员访问运算符(5.2.5)时 (包括添加隐式“this->”的情况)。如果 成员访问运算符和限定id用于命名成员 (如在
p->T::m
)中,命名成员的类是由
限定id的嵌套名称说明符(即T)-结束说明
]
这个注释的意思是,如果将this->
添加到C::x=2
然后C
是命名成员和成员的类
现在的问题是,谁是为C::x命名成员的类?
命名类由相同的11.2p5
指定:
对成员的访问受成员所在的类的影响
命名的此命名类是成员名称所在的类
查找并找到。
现在,在10.2中指定了类成员的名称查找,在阅读了所有内容后,我得出结论,x
是子对象集的并集,如下所示:
否则,新的S(f,C)是一个具有
声明和子对象集的并集
这意味着根据成员查找规则,x
可以来自B
或A
!这使得代码的格式错误为:Name
查找可能导致歧义,在这种情况下,程序的格式不正确。
但是,这种模糊性可以按照10.2p8解决:
歧义通常可以通过使用名称的类限定名称来解决
名字
从中我们可以看出,这就是他们选择做的:
// If the member was a qualified name and the qualified referred to a
// specific base subobject type, we'll cast to that intermediate type
// first and then to the object in which the member is declared. That allows
// one to resolve ambiguities in, e.g., a diamond-shaped hierarchy such as:
//
// class Base { public: int x; };
// class Derived1 : public Base { };
// class Derived2 : public Base { };
// class VeryDerived : public Derived1, public Derived2 { void f(); };
// void VeryDerived::f() {
// x = 17; // error: ambiguous base subobjects
// Derived1::x = 17; // okay, pick the Base subobject of Derived1
// }
但是,请注意上面引号中的可以
:通常可以解决
。这意味着不一定要解决这些问题。因此,我认为,根据标准,代码应该失败为含糊不清或作为私有成员访问失败
编辑
关于can
的解释以及此处是否出现歧义,存在一些争议。我发现有人谈论这个问题。编译(尽管是同一个编译器)。看起来是一个GCC错误吗?有趣的是:相同的编译器,不同的结果。无论如何,除了一个bug,我想不出别的什么了。G++4.7.2在两个位置成功地编译了同一个源代码。复制粘贴错误。对不起,是叮当声。钻石问题与这个问题有什么关系?他创建菱形继承图的唯一场景是第二个,他说这很好@瓦莱里亚塔曼纽克:我不知道,C::x
非常清楚地涉及到C
@GManNickG:你确定吗?它命名为A::x
。它不命名C
子对象。如果要命名C
子对象的成员,请使用C*p=this;p->x
@BenVoigt:我不明白:C::x
应该解析为C::A::x
,还是我遗漏了什么?GCC似乎将其分解为B::A::x
,这不会导致sense@BenVoigt我可能误解了您的意思,但我认为C::x
应该专门指C
子对象中的A::x
,不要含糊不清,请参见example@BenVoight10.1 p5(在N3242草案中,C++11之前的最后一个公共草案)“在这样的格中,可以使用显式限定来指定所指的子对象。函数体C::f
可以引用每个L
子对象:void C::f(){A::next=B::next;}
如果没有A:
或B:
限定符,上述C::f
的定义将由于歧义而格式错误(“此类晶格”指的是基类多次出现的多继承层次结构)我不确定我是否同意您的阅读,您是否看到10.1 p5中的示例?似乎很清楚,即使没有this->
,也不意味着有任何歧义。(这里的情况与问题和您的答案中引用的情况几乎完全相同。)@StephenLin:请注意,可以使用
。我认为这意味着它不是强制性的。我相信这里的“can”一词意味着,如果提供了限定id,并且限定id解析为只有该名称的一个成员的子对象,则情况“可以解决”(但如果给定了限定id,仍有多个子对象具有相同的成员名称,则可能不是),这并不意味着标准允许自由裁量是否