2

サンプルコードは次のとおりです。

struct A
{
    virtual int operator & ( A & ) { return 0; }
};

struct B : public A {};
struct C : public A {};

struct D : public C, public B {};

int main()
{
    D d;
    std::cout << &d << std::endl;
    return 0;
}

VS 2008 では完全に動作しますが、GCC ではコンパイルに失敗します。

../src/TestCast.cpp: In function ‘int main()’:
../src/TestCast.cpp:26:16: error: request for member ‘operator&’ is ambiguous
../src/TestCast.cpp:15:14: error: candidates are: virtual int A::operator&(A&)
../src/TestCast.cpp:15:14: error:                 virtual int A::operator&(A&)
make: *** [src/TestCast.o] Error 1

私が見る限り、署名ではなく名前で operator& overload を検索するため、あいまいなオーバーロードを見つけてエラーを生成します。

問題は、標準で正しいかどうかです。そうでない場合、それを説明している段落はどれですか? GCCにこのコードを受け入れさせる方法はありますか(つまり、名前ではなく署名で検索します)。

ところで、私はこのコードを修正する方法を知っています。エラーが表示される理由を知りたいだけです。

4

3 に答える 3

4

あなたが引き起こしたのはダイヤモンド継承の問題であり、仮想継承で解決できる可能性があります。

A では virtual を宣言しましたが、これは と の両方でoperator&も定義されています。現在、多重継承を使用しているため、これらのメソッドは両方とも内部で定義されています。BCD

標準から (10.1複数の基本クラス)。

クラスは、派生クラスの直接の基本クラスとして 2 回以上指定してはなりません。[注: クラスは複数回間接基本クラスになることができ、直接および間接基本クラスになることができます。このようなクラスでできることは限られています。直接基底クラスの非静的データ メンバーおよびメンバー関数は、派生クラスのスコープ内で参照できません。ただし、静的メンバー、列挙型、および型は明確に参照できます。— エンドノート ] [例:

キーワード virtual を含まない基底クラス指定子は、非仮想基底クラスを指定します。キーワード virtual を含む基本クラス指定子は、仮想基本クラスを指定します。最も派生したクラスのクラス ラティスにおける非仮想基本クラスの個別の発生ごとに、最も派生したオブジェクト (1.8) には、その型の対応する個別の基本クラス サブオブジェクトが含まれます。virtual と指定された個別の基本クラスごとに、最も派生したオブジェクトには、その型の基本クラスのサブオブジェクトが 1 つ含まれます。[例: クラス タイプ C のオブジェクトの場合、C のクラス ラティス内の (非仮想) ベース クラス L の各別個の発生は、タイプ C のオブジェクト内の別個の L サブオブジェクトと 1 対 1 で対応します。上記で定義されたクラス C の場合、クラス C のオブジェクトは、以下に示すようにクラス L の 2 つのサブオブジェクトを持ちます。

L       L
|       |
A       B
  \   /
    C

図 3 — 非仮想ベース 5 このようなラティスでは、明示的な修飾を使用して、どのサブオブジェクトが意味されているかを指定できます。関数 C::f の本体は、各 L サブオブジェクトのメンバ next を参照できます。 void C::f() { A::next = B::next; } // 整形式 A:: または B:: 修飾子がないと、上記の C::f の定義はあいまいさ (10.2) のために整形式ではなくなります。

于 2013-08-23T12:26:28.240 に答える
0

このコードによって達成されていることをdiamond-inheritance問題と呼びます。簡単に説明すると、コンパイラは、コンパイル時にoperator &、B クラスの継承バージョンと C クラスの継承バージョンのどちらを呼び出すかを判断できないため、あいまいになります。

それを克服するには、クラス定義を次のように宣言します

struct B : virtual public A {};

これにより、クラス D で使用できる関数のコピーが 1 つだけになります。

于 2013-08-23T13:29:00.150 に答える
0

私が標準で見つけたもの:

3.4 名前検索 [basic.lookup]

1 名前検索規則は、文法が特定の規則によって議論されるコンテキストでそのような名前を許可する場合はどこでも、すべての名前 (typedef-names (7.1.3)、namespace-names (7.3)、および class-names (9.1) を含む) に均一に適用されます。名前検索は、名前の使用をその名前の宣言 (3.1) に関連付けます。名前検索は、名前の明確な宣言を見つけるものとします (10.2 を参照)。名前検索は、名前が関数名であることがわかった場合、複数の宣言を名前に関連付けることができます。宣言はオーバーロードされた関数のセットを形成すると言われています (13.1)。オーバーロードの解決 (13.3) は、名前のルックアップが成功した後に行われます。アクセス ルール (条項 11) は、名前の検索と関数のオーバーロードの解決 (該当する場合) が成功した場合にのみ考慮されます。名前のルックアップ、関数オーバーロードの解決 (該当する場合)、およびアクセス チェックが成功した後でのみ、名前の宣言によって導入された属性が、式の処理 (5 節) でさらに使用されます。

于 2013-08-23T12:37:37.530 に答える