14

考虑 :

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

4

3 回答 3

10

这肯定是一个错误。没有理由从 class 继承B也应该改变C's 成员的可访问性。

甚至 GCC 4.8.0 (beta) 似乎都没有解决这个问题。另一方面,Clang 3.2 和 ICC 13.0.1正确地拒绝编译此代码

于 2013-03-13T23:43:13.217 回答
3

答案是clang是正确的。但是,根据标准,代码也可能因模棱两可而失败。

如果您查看 11.2p5,它有一个相关的注释(是的,我知道注释是非规范性的):

[ 注意:此类可以是显式的,例如,当使用限定 ID 时,也可以是隐式的,例如,当使用类成员访问运算符 (5.2.5) 时(包括添加隐式“this->”的情况)。如果同时使用类成员访问运算符和限定 ID 来命名成员(如 中p->T::m),则命名成员的类是由限定 ID 的嵌套名称说明符表示的类(即 T) . ——尾注]

这个注释的意思是,如果你添加this->C::x = 2;thenC是命名成员的类,并且gcc 4.7.2 在这种情况下正确失败

现在的问题是为谁命名该成员的类 C::x?由naming class相同指定11.2p5

对成员的访问受该成员所在的类的影响。这个命名类是在其中查找和找到成员名称的类。

现在,在 10.2 中指定了类成员的名称查找,在阅读完所有内容后,我得出的结论x是子对象集的并集,如下所示

否则,新的 S(f, C) 是具有共享声明集和子对象集并集的查找集。

这意味着根据成员查找规则x可以是 fromB A! 这使得代码格式错误:Name lookup can result in an ambiguity, in which case the program is ill-formed. 但是,这种歧义可以根据 10.2p8 解决:

歧义通常可以通过使用类名限定名称来解决。

Clang 源代码中,我们可以看到他们选择这样做:

// 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对于此处是否存在歧义的解释以及是否存在歧义,存在一些争论。我找到了缺陷报告 39。冲突的歧义规则讨论了这个问题。

于 2013-03-14T22:27:38.840 回答
-1

我不确定确切的原因,但我知道这一点:

当您在像这样的菱形图案中使用多重继承时,您将在派生对象 D 中拥有基类 A 的多个副本。

在您的情况下,对象 D 有 2 个名为 A::x 的成员,这可能会导致编译器混淆。

这被称为钻石问题

于 2013-03-13T23:45:01.313 回答