基本型へのポインターが実際に派生型のインスタンスを指している限り、そのような使用法は C++ 標準に従って未定義ではありません。ただし、コードサンプルでは、ポインターb
は のインスタンスB
またはその派生型 (存在しないもの) を指しておらず、 のインスタンスを指していますA
。したがって、コードは実際には未定義の動作を呼び出します。
私は、それがUBにつながる可能性があるというSOの答えを見つけました。それをテストし、コンパイルして正しく動作させました。
一部のコードがコンパイルされて正しく動作するという事実は、コードが未定義の動作を呼び出す可能性を排除するものではありません。未定義の動作には「動作しているように見える」ものが含まれるためです。未定義の動作を避ける必要があるのは、次に UB を呼び出したときに同じように動作するという保証がないためです。
未定義の動作ですか?もしそうなら、この問題への正しいアプローチは何でしょうか?
あなたのサンプルでは、はい、未定義の動作です。提供する例はせいぜい学術的な例であるため、正しいアプローチは、コードが実際に何をすべきかによって異なります。
明確にするために、関数に対する次の変更には明確にmain()
定義された動作があり、C++ 標準によって明示的に許可されています。
B objectB;
A* ptrA = &objectB;
B* b = static_cast<B*>(ptrA);
b->Show();
ここでは、ポインター自体の型が であっても、ポインターptrA
が実際には のインスタンスを指しているため、適切に定義されています。上記の例では、 から にキャストしてから、キャストされたポインターで の関数の1 つを呼び出します。違いは、質問の例では、実際には のインスタンスを指していないことです。B
A*
A*
B*
B
b
B
関連する条項(強調鉱山):
C++ 標準 5.2.9/8 静的キャスト [expr.static.cast]
タイプ「cv1 B
へのポインター」の右辺値 (B
はクラス タイプ) は、タイプ「cv2 D
へのポインター」D
( は から派生したクラス (第 10 節))の右辺値に変換できB
ます。D
」から「へのポインターB
」が存在し(4.10)、cv2は cv1 と同じ
cv修飾であるか、 cv1より大きいcv修飾であり、 の仮想基底クラスではありません。null ポインター値 (4.10) は、変換先の型の null ポインター値に変換されます。タイプ「cv1 へのポインター」の右辺値がB
D
B
B
これは実際には type のオブジェクトのサブオブジェクトでありD
、結果のポインターは type の外側のオブジェクトを指しますD
。それ以外の場合、キャストの結果は未定義です。