答えは、clang が正しいです。ただし、標準によれば、コードがあいまいで失敗する可能性もあります。
11.2p5 を見ると、関連するメモがあります (はい、メモが規範的でないことはわかっています)。
[ 注: このクラスは、たとえば修飾 ID が使用される場合などに明示的にすることも、クラス メンバー アクセス演算子 (5.2.5) を使用する場合などに暗黙的に行うこともできます (暗黙的な「this->」が追加される場合を含む)。 )。クラス メンバー アクセス演算子と修飾 ID の両方を使用してメンバーに名前を付ける場合 ( のようにp->T::m
)、メンバーに名前を付けるクラスは、修飾 ID (つまり、T) のネストされた名前指定子によって示されるクラスです。 . —終わりのメモ]
このメモが意味することは、追加this->
するとC::x = 2;
、C
メンバーに名前を付けるクラスであり、この場合、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を見つけました。相反する曖昧さのルールがこの問題について語っています。