そのようなキャストが許可されている型 (たとえば、T1
が POD 型でT2
isの場合unsigned char
) の場合、 を使用したアプローチstatic_cast
は標準によって明確に定義されています。
On the other hand, reinterpret_cast
is entirely implementation-defined - the only guarantee that you get for it is that you can cast a pointer type to any other pointer type and then back, and you'll get the original value; and also, you can cast a pointer type to an integral type large enough to hold a pointer value (which varies depending on implementation, and needs not exist at all), and then cast it back, and you'll get the original value.
To be more specific, I'll just quote the relevant parts of the Standard, highlighting important parts:
5.2.10[expr.reinterpret.cast]:
reinterpret_cast によって実行されるマッピングは実装定義です。[注: 元の値とは異なる表現を生成する場合と生成しない場合があります。] ... オブジェクトへのポインターは、異なる型のオブジェクトへのポインターに明示的に変換できます。 「T1 へのポインター」を「T2 へのポインター」型 (ここで、T1 と T2 はオブジェクト型であり、T2 のアラインメント要件は T1 のアラインメント要件よりも厳密ではありません) に変換し、元の型に戻すと、元のポインター値が返され、結果はこのようなポインタ変換の定義は未規定です。
だから、このようなもの:
struct pod_t { int x; };
pod_t pod;
char* p = reinterpret_cast<char*>(&pod);
memset(p, 0, sizeof pod);
は事実上未指定です。
なぜstatic_cast
機能するのかを説明するのは、もう少しトリッキーです。static_cast
これは、標準で意図されたとおりに常に動作することが保証されていると私が信じている使用するために書き直された上記のコードです。
struct pod_t { int x; };
pod_t pod;
char* p = static_cast<char*>(static_cast<void*>(&pod));
memset(p, 0, sizeof pod);
繰り返しますが、標準のセクションを引用して、一緒に、上記が移植可能であるべきであると結論付けます。
3.9[basic.types]:
POD 型 T のオブジェクト (基本クラスのサブオブジェクト以外) の場合、オブジェクトが型 T の有効な値を保持しているかどうかに関係なく、オブジェクトを構成する基になるバイト (1.7) を char または unsigned の配列にコピーできます。文字。char または unsigned char の配列の内容がオブジェクトにコピーされた場合、オブジェクトはその後元の値を保持します。
型 T のオブジェクトのオブジェクト表現は、型 T のオブジェクトによって使用される N 個の unsigned charオブジェクトのシーケンスです。ここで、N は sizeof(T) に等しくなります。
3.9.2[basic.compound]:
cv 修飾 (3.9.3) または cv 修飾されていない型void*
(void へのポインター) のオブジェクトを使用して、未知の型のオブジェクトを指すことができます。Avoid*
は、任意のオブジェクト ポインターを保持できる必要があります。cv-qualified または cv-unqualified (3.9.3)void*
には、 cv-qualified または cv-unqualified と同じ表現およびアラインメント要件が必要ですchar*
。
3.10[basic.lval]:
プログラムが、次のいずれかの型以外の左辺値を介してオブジェクトの格納された値にアクセスしようとした場合、動作は未定義です):
- ...
- char または unsigned char 型。
4.10[conv.ptr]:
「cv T へのポインター」型 (T はオブジェクト型) の右辺値は、「cv void へのポインター」型の右辺値に変換できます。「cv T へのポインター」を「cv void へのポインター」に変換した結果は、オブジェクトが型 T の最派生オブジェクト (1.8) であるかのように、型 T のオブジェクトが存在する格納場所の先頭を指します。 (つまり、基本クラスのサブオブジェクトではありません)。
5.2.9[expr.static.cast]:
左辺値から右辺値への変換 (4.1)、配列からポインタへの変換 (4.2)、関数からポインタへの変換 (4.3)、およびブール値への変換 (4.12) 以外の標準変換シーケンス (節 4) の逆を実行できます。 static_cast を明示的に使用します。
[編集]一方、この宝石があります:
9.2[class.mem]/17:
reinterpret_cast を使用して適切に変換された POD 構造体オブジェクトへのポインターは、その最初のメンバー (または、そのメンバーがビットフィールドの場合は、それが存在するユニット) を指し、その逆も同様です。[注:したがって、POD 構造体オブジェクト内に名前のないパディングがある場合がありますが、適切な配置を実現するために必要に応じて、先頭にはありません。]
これはreinterpret_cast
、ポインタ間がどういうわけか「同じアドレス」を意味することを暗示しているようです。図に行きます。