web-dev-qa-db-ja.com

reinterpret_castを使用する場合

私はreinterpret_caststatic_castの適用性について少し混乱しています。私が読んだことから、コンパイル時に型を解釈できるときは静的キャストを使うことが一般的な規則であり、それゆえWord staticです。これは、C++コンパイラが暗黙的キャストにも内部的に使用するキャストです。

reinterpret_castsは2つのシナリオ、整数型からポインタ型への変換、またはその逆の変換、あるいはあるポインタ型から別のポインタ型への変換に適用できます。私が得る一般的な考え方は、これは移植不可能であり、避けるべきです。

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

static_castreinterpret_castの両方の使い方を見たことがありますか?私が読んできたものから、キャストはコンパイル時に起こる可能性があるのでstaticのほうが良いようですが?あるポインタ型から別のポインタ型に変換するためにreinterpret_castを使うことを言っていますが?

401
HeretoLearn

C++標準は次のことを保証しています。

static_castを出入りするポインタをvoid*すると、アドレスが保持されます。つまり、以下では、a、b、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);

aとcは同じ値を含みますが、bの値は指定されていません。 (実際には通常、aとcと同じアドレスを含みますが、標準では指定されていません。さらに複雑なメモリシステムを搭載したマシンでは正しくない場合があります。)

Void *との間でキャストする場合は、static_castが優先されます。

388
jalf

reinterpret_castが必要な場合の1つのケースは、不透明(OPAQUE)型とのインターフェース時です。これは、プログラマが制御できないベンダー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; }
138
jwfearn

簡単な答え:reinterpret_castの意味がわからない場合は、使用しないでください。あなたが将来それを必要とするならば、あなたは知っているでしょう。

全回答:

基本的な数値タイプを考えましょう。

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

一方、reinterpret_castを呼び出すと、CPUは計算を呼び出しません。それはちょうどそれが別の型を持っているかのようにメモリ内のビットのセットを扱います。そのため、このキーワードを使用して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(big)または0000'0001'0000'0000(リトル・エンディアン)になります。 ) pポインタの下のバイトを再解釈キャストした後、それぞれ0000'0000または0000'0001になります。静的キャスティングを使用する場合、どのエンディアンが使用されていても、常に0000'0001になります。

編集:

最初のバージョンでは、例関数is_little_endianconstexprにしました。最新のgcc(8.3.0)でうまくコンパイルされていますが、規格では違法であると言われています。 clangコンパイラはそれをコンパイルすることを拒否します(これは正しいです)

76
jaskmar

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

あなたが説明する場合、そしてあなたがreinterpret_castを考えるかもしれないほとんどすべての場合のために、あなたは代わりにstatic_castあるいは他の何か代わりを使うことができます。他のものの間で規格はこれがあなたがstatic_cast(5.2.9)に何が期待できるかについて言うためにこれを持っています:

型 "cv voidへのポインタ"の右辺値は、オブジェクト型へのポインタに明示的に変換することができます。 objectへのポインタ型の値を「cv voidへのポインタ」に変換してから元のポインタ型に戻すと、元の値になります。

そのため、あなたのユースケースでは、標準化委員会があなたがstatic_castを使うことを意図していたことはかなり明白に思えます。

18
flodin

Reinterpret_castの用途の1つは、(IEEE 754)フロートにビット単位の演算を適用したい場合です。その一例が、Fast Inverse Square-Rootトリックです。

https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

浮動小数点数の2進表現を整数として扱い、それを右にシフトして定数から引き算し、それによって指数を半分にして否定します。 floatに変換した後、この近似をより正確にするために、Newton-Raphson反復を受けます。

float Q_rsqrt( float number )
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking
    i  = 0x5f3759df - ( i >> 1 );               // what the deuce? 
    y  = * ( float * ) &i;
    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

    return y;
}

これはもともとCで書かれていたので、Cキャストを使用しますが、類似のC++キャストはreinterpret_castです。

11
Adam P. Goucher

コンパイル時に継承をチェックするためにreinterprete_castを使うことができます。
ここを見てください: コンパイル時に継承をチェックするためにreinterpret_castを使う

3
Martin R.

まずintのような特定の型のデータがあります。

int x = 0x7fffffff://==nan in binary representation

次に、floatのような他の型と同じ変数にアクセスしたいとします。

float y = reinterpret_cast<float&>(x);

//this could only be used in cpp, looks like a function with template-parameters

または

float y = *(float*)&(x);

//this could be used in c and cpp

簡単:同じメモリが異なるタイプとして使用されていることを意味します。ですから、上記のようなint型としてのfloatのバイナリ表現をfloatに変換することができます。たとえば、0x80000000は-0です(仮数と指数はNULLですが、符号(msb)は1です。これは倍精度と倍精度の倍精度でも機能します。

最適化:多くのコンパイラでreinterpret_castが最適化されると思いますが、cキャスティングはポインタ演算によって行われます(値はメモリにコピーする必要があり、ポインタはcpuレジスタを指すことができません)。

注:どちらの場合も、キャストする前にキャスト値を変数に保存する必要があります。このマクロは役に立ちます。

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; })
1
cmdLP
template <class outType, class inType>
outType safe_cast(inType pointer)
{
    void* temp = static_cast<void*>(pointer);
    return static_cast<outType>(temp);
}

私はテンプレートを使用して簡単な安全なキャストを締めくくろうとしました。この解決法は関数へのポインタのキャストを保証しないことに注意してください。

1