答案是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。冲突的歧义规则讨论了这个问题。