19

私が遭遇したこの質問の微調整。検討:

class A {};

class B : private A {
   static void foo();
};

void B::foo(){
   B* bPtr1 = new B;
   A* aPtr1 = dynamic_cast<A*>(bPtr1); // gives pointer
   B* bPtr2 = dynamic_cast<B*>(aPtr1); // gives NULL
}

aPtr1は実際にはタイプであり、からのB*完全なアクセスBと継承がAあるため、両方のキャストが機能することを期待していました。しかし、そうではありません。なぜ?このキャストを達成する別の方法はありますか?

ご了承ください:

  • Bのメンバーでなければfoo()、両方のキャストが失敗します。
  • 公にB継承する場合は、両方のキャストが機能します。A
4

2 に答える 2

16

5.2.7(ISO / IEC 14882、2003年12月29日)は、この点でかなり明確です。

【表現についてdynamic_cast<T>(v)

Tが「pointertocv1 」であり、タイプが「pointer to cv2 であり、の基本クラスである場合、結果は、が指すオブジェクトの一意のサブオブジェクトへのポインタになります。[... bla bla about cv1 and cv2 ...]そしてBは、アクセス可能な明確なDの基本クラスでなければなりません(私の強調)Bv DBDBDv

(11.2 「基本クラスの発明されたパブリックメンバーがアクセス可能である場合、基本クラスはアクセス可能であると言われます。」を思い出してください。 )

これは、最初のキャストが機能する理由を説明しています。さて、2番目に:

[...]

それ以外の場合は、実行時チェックが適用され、によってポイントまたは参照されるオブジェクトが、によってvポイントまたは参照されるタイプに変換できるかどうかが確認されTます。

実行時チェックは、論理的に次のように実行されます。

  • が指す(参照する)最も派生したオブジェクトで、オブジェクトの基本クラスのサブオブジェクトをv指すv(参照する)場合、およびが指す(参照する)サブオブジェクトからT型のオブジェクトが1つだけ派生する場合、結果は、その オブジェクトへのポインタ(参照する左辺値)です。publicTvT
  • それ以外の場合、最も派生したオブジェクトの基本クラスサブオブジェクトをv指す(参照する)場合public、最も派生するオブジェクトのタイプにT明確なタイプの基本クラスがありpublic、結果はポインター(参照する左辺値)になります。 )T最も派生したオブジェクトのサブオブジェクトに。
  • それ以外の場合、実行時チェックは失敗します。

失敗したポインター型へのキャストの値は、必要な結果型のnullポインター値です。参照型へのキャストが失敗すると、bad_cast(18.5.2)がスローされます。

したがって、観察する動作はprivate継承によるもののようです。基本クラスがアクセス可能であっても、パブリックではなく、標準ではパブリックが必要であり、アクセスできません。

迷惑ですね。私はC++0xドラフトを手元に持っていません。状況が変わった場合に備えて、誰かが私の答えを引用符で編集できるかもしれません。

このキャストを達成する別の方法はありますか?

それはあなたが何をしたいかによります。基本的に、プライベート継承は、構成を実行するための単なる別のデバイスです。本当にプライベート派生インスタンスへのポインタを返す場合は、継承をパブリックにするか、メンバーを返します。

とにかく、あなたはそれstatic_castがこの制限を持っていないように見えることを知って幸せになるでしょう:

5.2.9。[約static_cast<T>(v)][...]

タイプ「pointertocv1B」(Bはクラスタイプ)の右辺値は、タイプ「pointer to cv2 D」の右辺値に変換できます。ここで、Dは、有効な標準の場合、 Bから派生したクラス(10節)です。 「ポインターからD」から「ポインターからB」への変換が存在し(4.10)、cv2はcv1と同じかそれ以上のcv資格であり、BはDの仮想基本クラスではありません。nullポインター値(4.10)は、宛先タイプのnullポインター値に変換されます。タイプ「pointertocv1B」の右辺値が、実際にはタイプDのオブジェクトのサブオブジェクトであるBを指している場合、結果のポインターはタイプDの囲んでいるオブジェクトを指します。それ以外の場合、キャストの結果は未定義です。 。

したがって、ポインタの実際の動的タイプが何であるかを確実に知っている場合は、static_cast内部に入ることができますfoo

この不整合が存在する理由についての追加情報に興味があります。

于 2011-08-03T14:26:26.210 に答える
1

Aには仮想関数がないため、これらは機能しません。ダウンキャストを実行する場合、それは簡単です。コンパイラは、おそらくチェックを行うことすらしません。アップキャストを作成する場合、コンパイラはチェックする必要がありますが、仮想関数がある場合にのみ機能するように定義されています。そうしないと、コンパイラはチェックに失敗し、結果はになりNULLます。

継承保護レベルおよびその他のアクセシビリティの問題は問題と直交しており、コンパイル時にのみ存在します。プログラムがコンパイルされると、正常に機能します。

ご了承ください:

foo()がBのメンバーでない場合、両方のキャストは失敗します。

BがAから公に継承する場合、両方のキャストが機能します。

それは真実ではありません。foo()RTTI機能にはまったく関係がありません。仮想ではなく、インスタンスメンバーでもありません。BがAから公に継承する場合、Aにはまだ仮想関数がなく、機能しません。

于 2011-08-03T14:27:43.103 に答える