15

この質問は、ここのコメントに触発されています。

次のコード スニペットを検討してください。

struct X {}; // no virtual members
struct Y : X {}; // may or may not have virtual members, doesn't matter

Y* func(X* x) { return dynamic_cast<Y*>(x); }

何人かの人々は、コンパイラが の本体を拒否するだろうと提案しましたfunc

ただし、これが標準で定義されているかどうかは、の実行時の値に依存するようですx。セクション 5.2.7 ( [expr.dynamic.cast]) から:

  1. 式の結果は、式を typedynamic_cast<T>(v)に変換した結果です。完全なクラス型へのポインタまたは参照、または「cvへのポインタ」でなければなりません。演算子は constness をキャストしてはなりません。vTT voiddynamic_cast

  2. Tがポインタ型である場合は、v完全なクラス型へのポインタの prvalue であり、結果は type の prvalue ですTTが左辺値参照型である場合v、 は完全なクラス型の左辺値であり、結果は によって参照される型の左辺値になりTます。Tが右辺値参照型である場合v、 は完全なクラス型を持つ式であり、結果は によって参照される型の xvalue になりTます。

  3. の型がvと同じか、 のクラス オブジェクト型が のクラス オブジェクト型よりも cv 修飾されていることを除いてTと同じである場合、結果は(必要に応じて変換されます) になります。TTvv

  4. の値がvポインター ケースの null ポインター値である場合、結果は type の null ポインター値になりますT

  5. T が「cv1 へのポインタ」であり、 の基底クラスである「cv2へのポインタ」型Bを持つ場合、結果は が指すオブジェクトの一意のサブオブジェクトへのポインタになります。同様に、T が「cv1への参照」の場合であり、 の基底クラスである型cv2を 持つ場合、結果は によって参照されるオブジェクトの一意のサブオブジェクトになります. 結果は が左辺値参照の場合は左辺値、 が右辺値参照の場合は xvalue です. ポインタとcv2の場合、プログラムの形式が正しくありません。v DBDBDv Bv DBDBDvTTがcv1よりも大きな cv-qualification を持っているか、Bがアクセスできないかあいまいな の基本クラスである場合D

  6. それ以外の場合vは、多相型へのポインターまたは左辺値でなければなりません。

  7. Tが「cv へのポインター」である場合void、結果は が指す最も派生したオブジェクトへのポインターになりますv。それ以外の場合は、 が指し示すまたは参照するオブジェクトを が指すまたは参照vする型に変換できるかどうかを確認するために、ランタイム チェックが適用されTます。これらは無視されます。vB

  8. Cがポイントまたは参照先のクラス型である場合T、実行時チェックは次のように論理的に実行されます。

    • が指す (参照する) 最も派生したオブジェクトで、オブジェクトの基底クラス サブオブジェクトを指す (参照する) 場合v、および結果ポイント (参照する) が指す (参照する) サブオブジェクトからタイプのオブジェクトが 1 つだけ派生する場合) そのオブジェクトに。vpublicCCvC

    • それ以外の場合、が最も派生したオブジェクトの基底クラス サブオブジェクトvを指し (参照し) public、最も派生したオブジェクトの型が型 の基底クラスを持ちC、それが明確であり、 である場合、結果は の サブオブジェクトをpublic指します (参照します) 。C最も派生したオブジェクト。

    • それ以外の場合、ランタイム チェック は失敗します

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

私がこれを読んだ方法では、ポリモーフィック型の要件は、上記の条件のいずれも満たされていない場合にのみ適用され、それらの条件の 1 つが実行時の値に依存します。

もちろん、いくつかのケースでは、コンパイラーは、入力が適切に NULL ではないことを積極的に判断できます (たとえば、thisポインターの場合)。ただし、ステートメントに到達すると判断できない限り、コンパイラーはコードを拒否できないと思います。 (通常は実行時の質問)。

もちろん、ここでは警告診断が重要ですが、コンパイラがこのコードをエラーで拒否するのは標準に準拠していますか?

4

3 に答える 3

3

その言い回しの意図は、コンパイル時に実行できるキャスト(upcastやdynamic_cast<Y*>((X*)0)など)もありますが、実行時チェックが必要なキャストもあると思います(この場合、ポリモーフィックタイプが必要です)。

コードスニペットが整形式である場合、それがnullポインター値であるかどうかを確認するために実行時チェックが必要になります。これは、実行時チェックが多態的な場合にのみ行われるべきであるという考えと矛盾します。

特定のキャストが実行時に延期されるのではなく、コンパイル時に不正な形式であることを明確にしたDR665を参照してください。

于 2012-07-02T21:58:01.960 に答える
3

非常に良い点です。

C++03 では、5.2.7/3 および 5.2.7/4 の文言は次のようになります。

3 vの型が必要な結果の型 (この説明では便宜上Rと呼びます) と同じである場合、またはRのクラスオブジェクト型がより cv 修飾されていることを除いてRと同じである場合vのクラス オブジェクト型。結果はvです (必要に応じて変換されます)。

4 vの値がポインターの場合の null ポインター値である場合、結果はR型の null ポインター値になります。

5.2.7/3 で導入された type への参照Rは、5.2.7/4 が 5.2.7/3 の副次句であることを意図していることを暗示しているようです。つまり、5.2.7/4 は、5.2.7/3 で説明されている条件、つまり型が同じ場合にのみ適用されることを意図しているように見えます。

ただし、C++11 での文言は異なり、もはや を含まないためR、5.2.7/3 と 5.2.7/4 の間の特別な関係を示唆するものではなくなりました。意図的に変えたのかな…

于 2012-07-02T21:46:01.220 に答える
1

私には、それはかなり明確なカットのようです。要件の列挙は「elseif..else if ..」タイプのものであるという誤った解釈をすると、混乱が生じると思います。

ポイント(1)と(2)は、cv-qualificationやlvalue-rvalue-prvalueなどの観点から、静的な入力タイプと出力タイプがどのように許可されるかを定義するだけです。これは些細なことであり、すべての場合に当てはまります。

ポイント(3)は非常に明確です。入力タイプと出力タイプの両方が同じである場合(cv修飾子を追加)、変換は簡単です(なし、またはcv修飾子を追加しただけです)。

ポイント(4)は、入力ポインターがnullの場合、出力ポインターもnullであることを明確に要求しています。この点は、(静的分析を介して)キャストを拒否または受け入れるという問題としてではなく、入力ポインターから出力ポインターへの変換が通常は実際のポインタ値(多重継承クラス階層では可能です)では、ポインタの「ヌル性」を維持するために、入力ポインタがnullの場合はそのオフセットを適用しないでください。これは、動的キャストが実行されるときに、ポインターがnullであるかどうかがチェックされ、nullの場合、結果のポインターにもnull値が必要であることを意味します。

ポイント(5)は、それがアップキャスト(派生からベースへ)である場合、キャストは静的に解決される(と同等static_cast<T>(v))ことを示しています。これは主に、アップキャストが整形式である場合(脚注が示すように)を処理するためですが、v(たとえば、v actualが、クラスTが複数回出現する複数の基本クラスを持つ派生オブジェクトを指している場合)。言い換えれば、これは、それがアップキャストである場合、ランタイムメカニズムなしで静的に実行することを意味します(したがって、発生しないはずの潜在的な障害を回避します)。この場合、コンパイラーは、それがであるかのように同じ基準でキャストを拒否する必要がありstatic_cast<T>(v)ます。

ポイント(6)では、明らかに、「その他」はポイント(5)を直接指します(そして確かにポイント(3)の些細なケースを指します)。意味(ポイント(7)とともに)、キャストがアップキャストではない場合(およびアイデンティティキャストではない場合(ポイント(3)))、それはダウンキャストであり、実行時に解決する必要があります、(vの)型がポリモーフィック型(仮想関数を持つ)であるという明示的な要件があります。

コードは、標準に準拠したコンパイラによって拒否される必要があります。私にとって、それについては疑いの余地はありません。なぜなら、キャストはダウンキャストであり、vのタイプは多形ではないからです。規格で定められた要件を満たしていません。nullポインター句(ポイント(4))は、コードが受け入れられるかどうかとは実際には関係ありません。キャスト全体でnullポインター値を保持することと関係があります(そうでない場合、一部の実装では(愚かな) )値がnullの場合でも、キャストのポインタオフセットを適用することを選択します)。

もちろん、彼らは別の選択をすることができ、ベースタイプがポリモーフィックでない場合、キャストがベースから派生への静的キャストとして動作することを許可しました(つまり、ランタイムチェックなしで)が、それは壊れていると思いますダイナミックキャストのセマンティクス。これは、「このキャストのランタイムチェックが必要です」と明確に言っています。そうでない場合は、ダイナミックキャストを使用しません。

于 2012-07-02T23:29:21.963 に答える