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

GCC 4.8.0 (ベータ版) でさえ、この問題を解決していないようです。一方、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;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を見つけました。相反する曖昧さのルールがこの問題について語っています。

于 2013-03-14T22:27:38.840 に答える
-1

正確な原因はわかりませんが、次のことはわかっています。

このようなひし形パターンで多重継承を使用すると、派生オブジェクト D に基本クラス A の複数のコピーが作成されます。

あなたの場合、オブジェクト D には A::x という名前の 2 つのメンバーがあり、コンパイラから混乱を招く可能性があります。

これはダイアモン問題として知られています

于 2013-03-13T23:45:01.313 に答える