web-dev-qa-db-ja.com

uintをintにreinterpret_castできないのはなぜですか?

これが私がやりたいことです:

const int64_t randomIntNumber = reinterpret_cast<int64_t> (randomUintNumber);

RandomUintNumberのタイプはuint64_t

エラーは(MSVC 2010)です:

エラーC2440: 'reinterpret_cast': 'constuint64_t'から 'int64_t'に変換できません1>変換は有効な標準変換であり、暗黙的に、またはstatic_cast、Cスタイルのキャスト、または関数スタイルのキャストを使用して実行できます。

なぜコンパイルされないのですか?どちらのタイプも同じビット長ですが、reinterpret_castの目的ではありませんか?

18
Violet Giraffe

それはreinterpret_castの目的ではないからです。 reinterpret_castで許可されているすべての変換には、整数型または列挙型がそれ自体に対してreinterpret_castである可能性があることを除いて、ポインターまたは参照が含まれます。これはすべて、標準[expr.reinterpret.cast]で定義されています。

ここで何を達成しようとしているのかはわかりませんが、randomIntNumberrandomUintNumberと同じ値を設定したい場合は、

const int64_t randomIntNumber = randomUintNumber;

その結果、コンパイラの警告が発生する場合、またはより明確にしたい場合は、次のようにします。

const int64_t randomIntNumber = static_cast<int64_t>(randomUintNumber);

randomUintNumberが2未満の場合、キャストの結果は入力と同じ値になります63。それ以外の場合、結果は実装定義ですが、int64_tを持つすべての既知の実装は、明らかなことを行うように定義することを期待しています。結果は、2を法とする入力と同等です。64


randomIntNumberrandomUintNumberと同じビットパターンを持たせたい場合は、次のようにすることができます。

int64_t tmp;
std::memcpy(&tmp, &randomUintNumber, sizeof(tmp));
const int64_t randomIntNumber = tmp;

int64_tは2の補数表現を使用することが保証されているため、実装がstatic_castの範囲外の値に対して、これと同じ結果になるようにuint64_tを定義することをhopeします。ただし、標準のAFAIKでは実際には保証されていません。

randomUintNumberがコンパイル時定数であっても、残念ながらここではrandomIntNumbernotコンパイル時定数です。しかし、それでは、コンパイル時定数はどの程度「ランダム」なのでしょうか。 ;-)

それを回避する必要があり、範囲外の符号なし値を符号付き型に変換することについて実装が賢明であると信頼できない場合は、次のようになります。

const int64_t randomIntNumber = 
    randomUintNumber <= INT64_MAX ? 
        (int64_t) randomUintNumber :
        (int64_t) (randomUintNumber - INT64_MAX - 1) + INT64_MIN;

今、私は可能な限り真にポータブルなコードを書くことに賛成ですが、それでもこれはパラノイアの危機に瀕していると思います。


ところで、あなたはこれを書きたくなるかもしれません:

const int64_t randomIntNumber = reinterpret_cast<int64_t&>(randomUintNumber);

または同等に:

const int64_t randomIntNumber = *reinterpret_cast<int64_t*>(&randomUintNumber);

int64_tuint64_tが存在する場合、同じサイズの符号付き型と符号なし型であることが保証されていますが、実際には標準整数の符号付きバージョンと符号なしバージョンであることが保証されていないため、これが機能することは完全には保証されません。タイプ。したがって、このコードが厳密なエイリアシングに違反しているかどうかは、実装固有です。厳密なエイリアシングに違反するコードの動作は未定義です。以下はnot厳密なエイリアシングに違反しており、randomUintNumberのビットパターンがlong longの値の有効な表現である場合は問題ありません。

unsigned long long x = 0;
const long long y = reinterpret_cast<long long &>(x);

したがって、int64_tuint64_tlong longunsigned long longのtypedefである実装では、私のreinterpret_castは問題ありません。そして、範囲外の値の符号付き型への実装定義の変換と同様に、実装が行うのが賢明なことは、それらを対応する符号付き/符号なし型にすることであるとexpectします。したがって、static_castや暗黙の変換と同様に、適切な実装で機能することを期待しますが、実際には保証されていません。

24
Steve Jessop

このような場合はstatic_castを使用してください。言語設計者は、すべての知恵で、reinterpret_castを正当化するのに「十分に安全ではない」とは見なされないと判断したと思います。

4
sheu

いいえそうではありません。 reinterpret_castは主に、既存のストレージビットを実際とは異なるタイプとして再解釈することを目的としています。これらの解釈の多くは実装に依存しており、標準ではreinterpret_cast(主に異なるポインター/参照型間でキャスト)で実行できる特定の(ここで引用するのはかなり長い)リストがリストされていますが、次のように述べています。

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

あなたの場合、既存のストレージの再解釈ではなく、タイプの変換が必要になる可能性があります。この目的にはstatic_castを使用します。

3
PlasmaHH

reinterpret_castは、オブジェクトのストレージを別のオブジェクトとして再解釈するために使用されます。標準的な表現を使用したくない場合は、reinterpret_castで実行できるすべてのことを見つけることができます ここ 。一般に、ポインタ型を操作する必要があることを覚えておいてください。

したがって、uint64_tに使用されているビットをint64_tとして本当に再解釈したい場合は、次のようにします。

int64_t randomIntNumber = reinterpret_cast<int64_t&> (randomUintNumber);

ただし、オブジェクトを変換するだけの場合は、可能であればその値を保持します...コンパイラが提案することを実行し、代わりにstatic_castを使用します。

1
Fiktik

C++ 11標準(N3376)5.2.10.1から:

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

許可される整数型から整数型への唯一の変換は、型が同一である場合に事実です。

その他にはポインタが含まれます。

1
Karthik T

なぜコンパイルされないのですか?

どちらの型もポインタではないからです。

両方のタイプのビット長は同じですが、

なぜそれが重要なのでしょうか?それらがポインタである場合、同じサイズのものを指していることが重要かもしれませんが、それらはポインタではありません。

reinterpret_castの目的ではありませんか?

いいえ、reinterpret_castはポインタキャスト用です。キャストの内側に&を置き、キャストの外側に*を置くことで、やりたいことができます。それがキャストの再解釈の目的です。

0
David Schwartz