次のシナリオを検討してください。
_std::array<int, 8> a;
auto p = reinterpret_cast<int(*)[8]>(a.data());
(*p)[0] = 42;
_
これは未定義の振る舞いですか?私はそう思う。
a.data()
は_int*
_を返しますが、これはint(*)[8]
と同じではありません。
タイプエイリアシングルール oncppreferenceは、_reinterpret_cast
_が無効であることを示唆しているようです
プログラマーとして、私はa.data()
が指すメモリ位置が_8
_ int
オブジェクトの配列であることを知っています。
この_reinterpret_cast
_を有効にするために私が見逃しているルールはありますか?
配列オブジェクトとその最初の要素はポインタ交換可能ではありません*したがって、reinterpret_cast
の結果は、「8 int
の配列へのポインター」タイプのポインターであり、その値は「a[0]
へのポインター」です。1つまり、タイプに関係なく、実際には配列オブジェクトを指していません。
次に、コードは、配列からポインターへの変換を、そのようなポインターの逆参照から生じた左辺値に適用します(インデックス式(*p)[0]
の一部として)2。その変換の動作は、左辺値が実際に配列オブジェクトを参照している場合にのみ指定されます3。この場合の左辺値はそうではないので、動作は省略によって定義されていません4。
*「配列オブジェクトとその最初の要素がポインタ相互変換できないのはなぜですか?」という質問の場合は、すでに質問されています: ポインタ相互変換と同じアドレスを持つ 。
1[expr.reinterpret.cast]/7 、 [conv.ptr]/2 、 [expr.static.cast]/1 およびを参照してください。 [basic.compound]/4 。
2[basic.lval]/6 、 [expr.sub] および [expr.add] を参照してください。
3[conv.array] : "結果は、配列の最初の要素へのポインタです。"
4[defns.undefined] :未定義の動作は、「このドキュメントが動作の明示的な定義を省略した場合」を含め、「このドキュメントが要件を課さない動作」です。
はい、動作は定義されていません。
_int*
_(a.data()
の戻り値の型)はint(*)[8]
とは異なる型であるため、厳密なエイリアシングルールに違反しています。
当然ですが(そしてこれは将来の読者の利益のためです)、
_int* p = a.data();
_
次の式_p + n
_と同様に、は完全に有効です。ここで、整数型n
は0から8までです。