BaseからDerived型へのキャストを行い、Base型が派生型のインスタンスではなく、結果がある場合にのみ結果を使用する場合、未定義の動作が発生しますか?
私が尋ねていることを理解するのは難しいですか?この例を見てください:
struct Animal { int GetType(){...} };
struct Dog : Animal { bool HasLoudBark(){...}};
struct Cat : Animal { bool HasEvilStare(){...} };
Animal * a = ...;
Dog* d = static_cast<Dog*>(a);
if(a->GetType() == DogType && d->HasLoudBark())
....
この場合、a
はDog
である場合とそうでない場合があります。常にstatic_cast
of a
〜Dog * d
しかし、d
であることが確実でない限り、Dog
は使用しません。
a
がDog
ではないと仮定すると、キャストの時点でのこの未定義の動作ですか?それとも、実際にd
でない限り、Dog
を実際に使用しないので、定義されていますか?
標準の関連部分への参照を歓迎します。
(はい、dynamic_castとRTTIを使用できることは知っています。おそらくこれは素晴らしいコードではありませんが、これが有効かどうかにもっと興味があります)
キャスト自体には未定義の動作があります。 C++ 17の引用(n4659)[expr.static.cast] 8.2.10/11:
タイプ「(pointer tocv1
B
」のprvalue(ここでB
はクラス型)は、タイプ「pointer tocv2D
」、ここでD
はB
から派生したクラス(13項)です。cv2がcv-qualificationと同じかそれより大きいcv-qualificationcv1。 ...「ポインタへのポインタcv1B
」の値が、実際にはB
型のオブジェクトのサブオブジェクトであるD
を指している場合、結果のポインタは囲んでいるオブジェクトを指しますタイプD
の。それ以外の場合、動作は未定義です。
これは未定義の動作です 、しかし(面白い)_reinterpret_cast
_の代わりに_static_cast
_を使用した場合、その悪魔を追い払うでしょう。
オブジェクトポインターは、異なるタイプのオブジェクトポインターに明示的に変換できます。オブジェクトポインタータイプのprvalue vがオブジェクトポインタータイプ「pointer to _
cv T
_」に変換されると、結果はstatic_cast<cv T*>(static_cast<cv void*>(v))
になります。
ユーザーAngewが述べたように、これは「_a == d
_のときにstatic_cast<void*>(d) == static_cast<void*>(a)
を保証する特定の内部表現が必要です」。
これは_[class.mem]/22
_から_26
_で表されます。
標準レイアウトクラスオブジェクトに非静的データメンバーがある場合、そのアドレスは、そのメンバーがビットフィールドでない場合、最初の非静的データメンバーのアドレスと同じです。そのアドレスは、その基本クラスのサブオブジェクトのそれぞれのアドレスとも同じです。
したがって、GetType()
of Animal
が common initial sequence の非静的データメンバーの値を返す場合Animal
およびDog
、動作が定義されています。
これらの要件は、単純な継承およびデフォルトで整列されたオブジェクトを処理するときに満たされます。