考虑一下:
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 version 4.7.2 (GCC)
,linux x86_64,如果这很重要的话。
编辑:不能使用Clang 3.2进行编译:clang 3.2
但它适用于gcc 4.7.2:gcc 4.7.2
答案 0 :(得分:10)
这肯定是错误。没有理由继承类B
也应该改变C
成员的可访问性。
甚至GCC 4.8.0(测试版)似乎都没有解决这个问题。另一方面,Clang 3.2和ICC 13.0.1 correctly refuse to compile this code。
答案 1 :(得分:3)
答案是铿锵有力的。但是,根据标准,代码也可能会失败。
如果你看11.2p5它有一个相关的注释(是的,我知道注释是非规范性的):
[注意:此类可以是显式的,例如,当使用qualified-id时, 或隐式的,例如,当使用类成员访问运算符(5.2.5)时 (包括添加隐式“this->”的情况)。 如果是一个班级 成员访问运算符和qualified-id用于命名成员 (如在
p->T::m
中),命名成员的类是由表示的类 qualified-id的嵌套名称说明符(即T)。 - 注意 ]
本说明的含义是,如果您将this->
添加到C::x = 2;
,则C
是指定该成员和gcc 4.7.2 correctly fails when this is the case的类。
现在问题是谁是命名 C::x
成员的班级?
naming class
由相同的11.2p5
:
对成员的访问权限受成员所在的类的影响 命名。 此命名类是成员名称所在的类 抬头找到了。
现在,在10.2中指定了类成员的名称查找,在阅读完所有内容之后,我得出结论:x
是子对象集的联合,符合:
否则,新的S(f,C)是具有共享集的查找集 声明和子对象集的并集。
这意味着根据成员查找规则x
可以来自B
或 A
!这使得代码格式不正确:Name
lookup can result in an ambiguity, in which case the program is ill-formed.
但是,这种歧义可以按照10.2p8:解决
通常可以通过使用其类来限定名称来解决歧义 名。
从Clang source,我们可以看到他们选择做的事情:
// 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
:often can be resolved
。这意味着他们不一定需要解决。因此,我认为根据标准,代码应该失败,因为模糊或私有成员访问失败。
修改强>
关于can
的解释以及此处是否出现歧义,存在一些争论。我发现Defect report 39. Conflicting ambiguity rules谈到了这个问题。
答案 2 :(得分:-1)
我不确定原因,但我知道:
当您在像这样的菱形图案中使用多重继承时,您将在派生对象D中拥有基类A的多个副本。
在您的情况下,对象D有2个名为A :: x的成员,这可能会导致编译器混淆。
这被称为Diamon Problem