web-dev-qa-db-ja.com

このreinterpret_castがコンパイルされないのはなぜですか?

という事は承知しています reinterpret_castは危険です。テストするためにこれを行っています。私は次のコードを持っています:

int x = 0;
double y = reinterpret_cast<double>(x);

プログラムをコンパイルしようとすると、次のエラーが表示されます

タイプ「float」からタイプ「double」への無効なキャスト

どうしたの? reinterpret_castは、リンゴを潜水艦に変換するために使用できる不正なキャストでしたが、この単純なキャストがコンパイルされないのはなぜですか?

61
Vlad the Impala

キャストによって返された値にyを割り当てることで、実際に値xをキャストするのではなく、変換しています。つまり、yxを指しておらず、floatを指しているふりをしません。変換は、タイプfloatの新しい値を構築し、xからの値を割り当てます。 C++でこの変換を行う方法はいくつかあります。

int main()
{
    int x = 42;
    float f = static_cast<float>(x);
    float f2 = (float)x;
    float f3 = float(x);
    float f4 = x;
    return 0;
}

最後の違い(暗黙の変換)である唯一の本当の違いは、より高い警告レベルでコンパイラ診断を生成します。しかし、それらはすべて機能的に同じことを行います-そして多くの場合実際は同じマシンコードのように同じことです。

本当にxがフロートであるふりをしたいのであれば、これを行うことでxをキャストしたいのです:

#include <iostream>
using namespace std;

int main()
{
    int x = 42;
    float* pf = reinterpret_cast<float*>(&x);
    (*pf)++;
    cout << *pf;
    return 0;
}

これがどれほど危険かを見ることができます。実際、マシンでこれを実行したときの出力は1、これは明らかに42 + 1ではありません。

41
John Dibling

C++でreinterpret_castは、言語仕様に明示的にリストされている特定の変換セットのみを実行できます。要するに、 reinterpret_castは、ポインターからポインターへの変換と参照から参照への変換(およびポインターから整数への変換、および整数からポインターへの変換)のみを実行できます。これは、キャストのまさに名前で表された意図と一致しています。それは、ポインター/参照の再解釈に使用されることを意図しています。

あなたがしようとしているのは再解釈ではありません。 intdoubleとして再解釈する場合は、参照型に変換する必要があります

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

同等のポインターベースの再解釈はおそらくより明示的ですが

double y = *reinterpret_cast<double*>(&x); // same as above

ただし、reinterpret_castは、参照/ポインター型を変換できます。結果の参照/ポインターを介してデータを読み取ろうとすると、未定義の動作が発生します。

そして、いずれにしても、これはもちろん、異なるサイズのintdoubleを備えたプラットフォームではあまり意味がありません(より大きなdoubleの場合は、 x)が占有するメモリを超えています。

したがって、最終的には、すべてが達成しようとしていたものに要約されます。記憶の再解釈?上記を参照。何らかの意味のあるintからdoubleへの変換?その場合、reinterpret_castはここであなたを助けません。

41
AnT

reinterpret_castは一般的なキャストではありません。 C++ 03仕様セクション5.2.10.1によると:

Reinterpret_castを使用して明示的に実行できる変換を以下にリストします。 reinterpret_castを使用して他の変換を明示的に実行することはできません。

そして、整数型と浮動小数点型の間の変換を説明するものはリストされていません(または、整数型の間でも、これは違法ですreinterpret_cast<long>(int(3));

11

intのビットをdoubleの表現に変換しようとしている場合、値ではなくaddressをキャストする必要があります。また、サイズが一致することを確認する必要があります。

uint64_t x = 0x4045000000000000;
double y = *reinterpret_cast<double *>(&x);
10
finnw

再解釈キャストを使用すると、メモリブロックを別の型として再解釈できます。これは、ポインターで実行する必要がありますまたは参照

_int x = 1;
float & f = reinterpret_cast<float&>(x);
assert( static_cast<float>(x) != f );   // !!
_

もう1つは、結果として奇妙な値が出たり、上記のアサートが失敗しないだけでなく、タイプが異なるサイズであり、「ソース」から「 「宛先」タイプ、再解釈された参照/ポインターの操作はsizeof(destination)バイトにアクセスします。 sizeof(destination)>sizeof(source)の場合、実際の変数メモリを超えて、アプリケーションを強制終了するか、ソースまたは宛先以外の他の変数を上書きする可能性があります。

_struct test {
   int x;
   int y;
};
test t = { 10, 20 };
double & d = reinterpret_cast<double&>( t.x );
d = 1.0/3.0;
assert( t.x != 10 ); // most probably at least.
asswet( t.y != 20 );
_

intdoubleは異なるサイズのオブジェクトである可能性があるため、コンパイラはナンセンスとして書いたものを拒否します。確かに危険ですが、この方法でも同じ効果を達成できます。

_int x = 0;
double y = *reinterpret_cast<double*>(&x);
_

xyが異なるサイズ(たとえば、intが4バイトで、doubleが8バイト)である場合、8バイトのメモリを逆参照すると、これは潜在的に危険です。 _&x_でyを埋めるために、4バイトのxと4バイトの...にメモリ内で次に来るもの(おそらくyの開始、またはガベージ) 、または完全に何か。)

整数をdoubleに変換する場合は、_static_cast_を使用すると、変換が実行されます。

xのビットパターンにアクセスする場合、便利なポインター型(たとえば、_byte*_)にキャストし、sizeof(int) / sizeof(byte)までアクセスします。

_byte* p = reinterpret_cast<byte*>(&x);
for (size_t i = 0; i < sizeof(int); i++) {
  // do something with p[i]
}
_
3
Dominic Cooney

再解釈のアプローチは、私に不定の結果をもたらす奇妙な道を導きました。結局、このようにmemcpyを使用する方がはるかに良いことがわかりました!

double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof(dest));
1
Evan Moran

reinterpret_castはポインターに最適です。そのため、1つのオブジェクトへのポインターを「潜水艦」に変えることができます。

msdn から:

Reinterpret_cast演算子は、char *からint *、One_class *からUnrelated_class *などの変換に使用できますが、これらは本質的に安全ではありません。

Reinterpret_castの結果は、元の型にキャストバックされる以外には安全に使用できません。他の用途は、せいぜい、携帯性がありません。

1
Alex B

Intをdoubleにキャストするのにキャストは不要です。コンパイラーは暗黙的に割り当てを実行します。

Reinterpret_castは、ポインタと参照で使用されます。たとえば、int *からdouble *

0
Matt Davis

それは面白い。おそらく、intからfloatへの暗黙的な変換を行ってから、キャストをdoubleにしようとしているのかもしれません。 int型とfloat型は、バイト単位で同じサイズになる傾向があります(もちろん、システムによって異なります)。

0
Andy White