37

私は本を​​読んでreinterpret_castいますが、直接使用するべきではなく、 void* と組み合わせてキャストする必要があることがわかりましたstatic_cast:

T1 * p1=...
void *pv=p1;
T2 * p2= static_cast<T2*>(pv);

それ以外の:

T1 * p1=...
T2 * p2= reinterpret_cast<T2*>(p1);

ただし、これが直接キャストよりも優れている理由の説明が見つかりません。誰かが私に説明をしてくれたり、答えを教えてくれたりすると、とても感謝しています。

前もって感謝します

ps何にreinterpret_cast使われるかは知っていますが、このように使われているのを見たことがありません

4

3 に答える 3

26

そのようなキャストが許可されている型 (たとえば、T1が POD 型でT2isの場合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、ポインタ間がどういうわけか「同じアドレス」を意味することを暗示しているようです。図に行きます。

于 2009-12-07T21:40:00.600 に答える
6

両方の形式が明確に定義されていることを意図していることに疑いの余地はありませんが、文言はそれを捉えることができません。

どちらの形式も実際には機能します。

reinterpret_cast意図についてより明確であり、優先する必要があります。

于 2011-12-13T08:17:49.853 に答える
4

これがそうである本当の理由は、C++ が継承を定義する方法と、メンバー ポインターのためです。

C では、ポインターはほとんど単なるアドレスです。C++ では、いくつかの機能のために、より複雑にする必要があります。

メンバー ポインターは実際にはクラスへのオフセットであるため、C スタイルを使用すると、それらをキャストすることは常に大惨事になります。

いくつかの具体的な部分も持つ 2 つの仮想オブジェクトを多重継承した場合、これも C スタイルの惨事です。ただし、これはすべての問題を引き起こす多重継承の場合であるため、とにかくこれを使用する必要はありません。

そもそもこれらのケースを使用しないことを本当に願っています。また、大量にキャストしている場合は、デザインを台無しにしている別の兆候です。

私が最後にキャストするのは、C++ が決定する領域のプリミティブが同じではなく、明らかにそれらが存在しなければならない場所である場合だけです。実際のオブジェクトの場合、何かをキャストしたいときはいつでも、ほとんどの場合「インターフェースへのプログラミング」を行う必要があるため、設計に疑問を投げかけます。もちろん、サードパーティの API の動作を変更することはできないため、常に多くの選択肢があるわけではありません。

于 2009-12-08T01:03:56.903 に答える