web-dev-qa-db-ja.com

不正なキャスト-未定義の動作であるキャストまたは使用

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())
    ....

この場合、aDogである場合とそうでない場合があります。常にstatic_cast of aDog * dしかし、dであることが確実でない限り、Dogは使用しません。

aDogではないと仮定すると、キャストの時点でのこの未定義の動作ですか?それとも、実際にdでない限り、Dogを実際に使用しないので、定義されていますか?

標準の関連部分への参照を歓迎します。

(はい、dynamic_castとRTTIを使用できることは知っています。おそらくこれは素晴らしいコードではありませんが、これが有効かどうかにもっと興味があります)

36
Mike Vine

キャスト自体には未定義の動作があります。 C++ 17の引用(n4659)[expr.static.cast] 8.2.10/11:

タイプ「(pointer tocv1B」のprvalue(ここでBはクラス型)は、タイプ「pointer tocv2D」、ここでDBから派生したクラス(13項)です。cv2がcv-qualificationと同じかそれより大きいcv-qualificationcv1。 ...「ポインタへのポインタcv1B」の値が、実際にはB型のオブジェクトのサブオブジェクトであるDを指している場合、結果のポインタは囲んでいるオブジェクトを指しますタイプDの。それ以外の場合、動作は未定義です。

36
Angew

これは未定義の動作です 、しかし(面白い)_reinterpret_cast_の代わりに_static_cast_を使用した場合、その悪魔を追い払うでしょう。

_[expr.reinterpret.cast]/7_

オブジェクトポインターは、異なるタイプのオブジェクトポインターに明示的に変換できます。オブジェクトポインタータイプの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_で表されます。

_[class.mem]/26_

標準レイアウトクラスオブジェクトに非静的データメンバーがある場合、そのアドレスは、そのメンバーがビットフィールドでない場合、最初の非静的データメンバーのアドレスと同じです。そのアドレスは、その基本クラスのサブオブジェクトのそれぞれのアドレスとも同じです。

したがって、GetType() of Animalcommon initial sequence の非静的データメンバーの値を返す場合AnimalおよびDog、動作が定義されています。

これらの要件は、単純な継承およびデフォルトで整列されたオブジェクトを処理するときに満たされます。

12
YSC