550

reinterpret_castvsの適用可能性について少し混乱していstatic_castます。私が読んだことから、一般的なルールは、コンパイル時に型を解釈できる場合は静的キャストを使用することstaticです。これは、C++ コンパイラが暗黙のキャストにも内部的に使用するキャストです。

reinterpret_castは、次の 2 つのシナリオに適用できます。

  • 整数型をポインタ型に、またはその逆に変換する
  • あるポインター型を別の型に変換します。私が得る一般的な考えは、これは移植性がなく、避けるべきだということです。

私が少し混乱しているのは、私が必要とする 1 つの使用法です。C から C++ を呼び出しており、C コードは C++ オブジェクトを保持する必要があるため、基本的にvoid*. void *とClass 型の間の変換に使用するキャストはどれですか?

static_castreinterpret_cast?の両方の使用法を見てきました。私が読んでいたことからstatic、コンパイル時にキャストが発生する可能性があるため、より良いように見えますか? reinterpret_castあるポインター型から別のポインター型に変換するために使用すると言われていますが?

4

11 に答える 11

519

C++ 標準では、次のことが保証されています。

static_castへのポインターとからのポインターを使用するとvoid*、アドレスが保持されます。つまり、次の 、abおよびcすべてが同じアドレスを指しています。

int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);

reinterpret_castポインターを別の型にキャストしreinterpret_castてから元の型に戻した場合に、元の値が得られることのみを保証します。したがって、次のとおりです。

int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);

acには同じ値が含まれていますが、 の値bは指定されていません。a(実際には、通常はおよび と同じアドレスが含まれますが、cこれは標準では指定されておらず、より複雑なメモリ システムを備えたマシンでは当てはまらない場合があります。)

との間のキャストにはvoid*static_castを優先する必要があります。

于 2009-02-21T16:42:12.010 に答える
184

必要なケースの 1 つreinterpret_castは、不透明なデータ型とやり取りする場合です。これは、プログラマが制御できないベンダー API で頻繁に発生します。以下は、ベンダーが任意のグローバル データを格納および取得するための API を提供する、不自然な例です。

// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData();

この API を使用するには、プログラマーはデータをキャストしてキャストしVendorGlobalUserData直す必要があります。 static_cast動作しません。使用する必要がありますreinterpret_cast:

// main.cpp
#include "vendor.hpp"
#include <iostream>
using namespace std;

struct MyUserData {
    MyUserData() : m(42) {}
    int m;
};

int main() {
    MyUserData u;

        // store global data
    VendorGlobalUserData d1;
//  d1 = &u;                                          // compile error
//  d1 = static_cast<VendorGlobalUserData>(&u);       // compile error
    d1 = reinterpret_cast<VendorGlobalUserData>(&u);  // ok
    VendorSetUserData(d1);

        // do other stuff...

        // retrieve global data
    VendorGlobalUserData d2 = VendorGetUserData();
    MyUserData * p = 0;
//  p = d2;                                           // compile error
//  p = static_cast<MyUserData *>(d2);                // compile error
    p = reinterpret_cast<MyUserData *>(d2);           // ok

    if (p) { cout << p->m << endl; }
    return 0;
}

以下は、サンプル API の不自然な実装です。

// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }
于 2009-02-21T19:06:14.473 に答える
155

簡単な答え:reinterpret_castの略かわからない場合は、使用しないでください。将来必要になる場合は、わかります。

完全な答え:

基本的な数の型を考えてみましょう。

たとえば、プロセッサに変換する場合int(12)unsigned float (12.0f)両方の数値のビット表現が異なるため、いくつかの計算を呼び出す必要があります。これがそのstatic_cast略です。

一方、reinterpret_castCPU を呼び出すと、計算は呼び出されません。メモリ内のビットのセットを、別のタイプがあるかのように扱うだけです。したがって、このキーワードを使用して変換int*するfloat*と、新しい値 (ポインターの参照解除後) は、数学的な意味で古い値とは何の関係もありません。

例:reinterpret_cast 1 つの理由 - バイト順 (エンディアン) のために移植性がないのは事実ですしかし、これは驚くべきことに、それを使用する最良の理由であることがよくあります。例を想像してみましょう: ファイルからバイナリ 32 ビット数を読み取る必要があり、それがビッグ エンディアンであることがわかっています。コードは汎用的である必要があり、ビッグ エンディアン (ARM など) およびリトル エンディアン (x86 など) システムで正しく動作する必要があります。したがって、バイトオーダーを確認する必要があります。コンパイル時によく知られているので、関数を書くことができconstexprます:これを達成する関数を書くことができます:

/*constexpr*/ bool is_little_endian() {
  std::uint16_t x=0x0001;
  auto p = reinterpret_cast<std::uint8_t*>(&x);
  return *p != 0;
}

説明:xメモリ内のバイナリ表現は0000'0000'0000'0001(ビッグ) または0000'0001'0000'0000(リトルエンディアン) の可能性があります。再解釈キャストした後、pポインターの下のバイトはそれぞれ0000'0000または0000'0001ます。静的キャストを使用すると0000'0001、使用されているエンディアンに関係なく、常に になります。

編集:

最初のバージョンでは、サンプル関数is_little_endianを にしconstexprました。最新の gcc (8.3.0) では正常にコンパイルされますが、標準では違法とされています。clang コンパイラはそれをコンパイルすることを拒否します (これは正しいです)。

于 2017-04-07T08:58:07.223 に答える
21

の意味はreinterpret_cast、C++ 標準では定義されていません。したがって、理論的には、 areinterpret_castはプログラムをクラッシュさせる可能性があります。実際には、コンパイラはあなたが期待することをしようとします。つまり、渡されたもののビットを、キャスト先の型であるかのように解釈します。使用するコンパイラが何をするかを知っていれば、それを使用できますが、移植可能reinterpret_cast であると言うのは嘘です。

あなたが説明したケース、およびあなたが考える可能性のあるほとんどすべてのケースでは、代わりにまたは他の代替手段reinterpret_castを使用できます。static_castとりわけ、標準では、期待できることについて次のように述べていますstatic_cast(§5.2.9):

「cv void へのポインター」型の右辺値は、オブジェクト型へのポインターに明示的に変換できます。オブジェクトへのポインター型の値が「cv void へのポインター」に変換され、元のポインター型に戻されると、元の値になります。

したがって、あなたのユースケースでは、標準化委員会があなたの使用を意図していたことは明らかですstatic_cast

于 2009-02-21T16:38:15.530 に答える
3

reinterprete_cast を使用して、コンパイル時に継承をチェックできます。
こちらをご覧ください: reinterpret_cast を使用してコンパイル時に継承をチェックする

于 2014-10-29T17:12:47.023 に答える
1
template <class outType, class inType>
outType safe_cast(inType pointer)
{
    void* temp = static_cast<void*>(pointer);
    return static_cast<outType>(temp);
}

結論として、テンプレートを使用して簡単なセーフ キャストを作成しました。このソリューションは、関数にポインターをキャストすることを保証しないことに注意してください。

于 2013-01-18T14:38:38.570 に答える
-19

よくある質問を読む! C で C++ データを保持するのは危険です。

C++ では、オブジェクトへのポインターはvoid *キャストなしで変換できます。しかし、その逆は正しくありません。static_cast元のポインタを戻すにはが必要です。

于 2009-02-21T16:31:02.333 に答える