15

この場合はどうなるでしょうか。

struct A {
  void f();
};

struct B : virtual A {
  using A::f;
};

struct C : virtual A {
  using A::f;
};

struct D : B, C { 
  void g() {
    f();
  }
};

関心のある行はです f()。明らかに、FDISfによるによるルックアップは成功し、を見つけます。ただし、過負荷解決ではどの候補が考慮されますか?仕様は次のように述べています:10.2A::f13.3.1p4

using-declarationによって派生クラスに導入された非変換関数の場合、関数は、暗黙のオブジェクトパラメータのタイプを定義する目的で、派生クラスのメンバーであると見なされます。

これの目的は、単一のクラスの場合、そのようなクラスに独自のメンバー関数と、基本クラス関数の名前をスコープに入れるusing宣言の両方が含まれている場合、過負荷解決中にすべての関数候補が暗黙のオブジェクトで同じクラスタイプを持つことです。パラメータ。しかし、これは上記の例にとって何を意味するのでしょうか?候補者は次のようになりますか?

void F1(B&)
void F2(C&)
// call arguments: (lvalue D)

によるルックアップ結果セットには宣言が1つしかないため、これは間違っているように見えます10.2p7。これをどのように解釈しますか?

4

4 に答える 4

1

10.2/7 で生成されたルックアップ セットは宣言が 1 つしかないため、関数のオーバーロードはまったく存在しないと思います。13.3.1/4 は、10.2/7 の結果のルックアップ セットに 2 つ以上の宣言が含まれている場合にのみ適用されます。

編集:おそらく、私が望んでいたほど明確ではありませんでした. fが でオーバーロードされたとしてもA、ほとんど同じ理由が当てはまると思います。一歩一歩進んでいくのが一番いいのかもしれません。(この場合、標準と同じ S(f, X) 表記を使用していますが、最も派生したクラスが D であるため、S(f, D) はそれらの S(f, C) に対応することに注意してください。 、および S(f, B) ans S(f, C) は、その S(f, B 1 ) および S(f, B 2 ) に対応します。

最初の s(f, D) は空です。これは、D に直接含まれる f の宣言がないためです。これに基づいて、10.2/5 に到達します。

10.2/6 では、s(f, B) を S(f, D) にマージすることから始めます。s(f, D) は現在空であるため、最初の箇条書きの下の 2 番目の条件に従い、S(f, D) は S(f, B) のコピーになります。

次に、S(f, C) を S(f, D) にマージする必要があります。この場合、S(f, C) の各サブオブジェクト メンバーは、S(f, D) のサブオブジェクト メンバーです。これは、最初の箇条書きの最初の条件を満たしているため、S(f, D) を変更せずにそのままにして、マージを完了します。

その時点で、考慮すべき基底クラス B iはもうないので、S(f, D) = S(f, B) となります。S(f, C) からの宣言は、最終的なオーバーロード セットにはまったく存在しません

次に、S(f, B) に 2 つ以上の関数が含まれている場合は、13.3.1 に進み、オーバーロード セットを解決しますが、セット全体が B 経由で取得されたため、質問で提示された状況は単純に存在しません。

于 2011-04-16T01:30:31.627 に答える
0

憶測だけで、まったくわかりません。:)

[ Example:
struct A { int x; }; // S(x,A) = { { A::x }, { A } }
struct B { float x; }; // S(x,B) = { { B::x }, { B } }
struct C: public A, public B { }; // S(x,C) = { invalid, { A in C, B in C } }
struct D: public virtual C { }; // S(x,D) = S(x,C)
struct E: public virtual C { char x; }; // S(x,E) = { { E::x }, { E } }
struct F: public D, public E { }; // S(x,F) = S(x,E)
int main() {
F f;
f.x = 0; // OK, lookup finds E::x
}
S(x, F) is unambiguous because the A and B base subobjects of D are also base subobjects of E, so S(x,D)
is discarded in the first merge step. —end example ]

10.2p7 の例でS(f,C)、ルックアップ セットを示します。最後に提供DEた文は不可欠Cです。 さて、あなたの例では、 の基本クラスの を隠すものは何もないため、 の使用はまだあいまいであり、10.2p7 があなたのケースにどのように適用されるかわかりません。上で述べたように、まったくわかりません。;)E::x xCF::x
fDD::f

于 2011-04-16T01:05:56.993 に答える
0

f質問に直接取り組む代わりに、各派生クラスに関数が存在するふりをしてもうまくいかないことを主張しようと思います。

候補関数は 1 つだけで、型は

void A::(void)

その関数へのメンバーへのポインターを形成することはできますが

void (A::*F0)(void) = &A::f;
void (B::*F1)(void) = F0;
void (C::*F2)(void) = F0;

これは、関数のパラメーターを計算するために必要な追加情報がメンバーへのポインターに含まれているためです。メンバーへのポインター呼び出しサイトはA、実際のターゲット インスタンスのサブオブジェクトを見つけて、 のthisポインターを提供しますfthis派生型のポインターから A のメンバーを見つけるためのロジックが関数内にありません。したがって、あなたの質問が示唆するように、そしてvoid F1(B* this)について話すことはできません。void F2(C* this)

関数が派生クラスのメンバーと見なされる場合、次のようになります。

void B::A::f(void);
void C::A::f(void);

しかし、B::AC::Aは同じ基本クラスであるため、リストに 2 回含まれていますが、最終的に候補リストには 1 つの関数しかありません。次に、仮想継承により、両方の候補が同じオブジェクトで同じ関数を呼び出していることが提供され、あいまいさはありません。

于 2011-04-16T04:00:12.350 に答える
0

鍵は 10.2p5 にあると思います。標準では、各「直接基底クラスのサブオブジェクト」をチェックすることが言及されています。

実質的に継承されるため、 (10.1p4)Aの「直接基底クラス サブオブジェクト」です。D

次に、 、、およびの 3 つのサブオブジェクトDが考慮されます。10.2p6 によりとが削除され (はこれらのベース)、 のみが候補です。ABCBCAA::f

于 2011-04-16T17:38:41.053 に答える