reinterpret_cast 変換
ベースとなるビットパターンを再解釈することによって型の間で変換します。
目次 |
[編集] 構文
reinterpret_cast < new_type > ( expression )
|
|||||||||
new_type
型の値を返します。
[編集] 説明
static_cast と異なり、しかし const_cast と同様に、 reinterpret_cast 式はいかなる CPU 命令へもコンパイルされません (整数とポインタの間で変換するときやポインタの表現が型によって異なるような変わったアーキテクチャのときを除きます)。 これは純粋に、 expression を new_type 型であるかのように扱うようコンパイラに指示する、コンパイル時の指令です。
以下の変換のみが reinterpret_cast を用いて行うことができます。 ただし cv 修飾を除去することはできません。
expression
の値と同じです。 (C++11以上)T1*
は、別のオブジェクトポインタ型 cv T2*
に変換できます。 これは static_cast<cv T2*>(static_cast<cv void*>(expression)) とまったく同等です (これは、もし T2
のアライメント要件が T1
より厳しくなければ、ポインタの値は変更されず、結果のポインタのその元の型への逆変換は、その元の値を生成します)。 いかなる場合でも、結果のポインタは、型エイリアスのルール (後述) によって許されている場合にのみ、安全に逆参照できます。 T1
型の lvalue 式は、別の型 T2
への参照に変換できます。 結果は、その元の lvalue と同じオブジェクトを参照する lvalue または xvalue ですが、異なる型を持ちます。 一時オブジェクトは作成されず、コピーは行われず、コンストラクタや変換関数は呼ばれません。 結果の参照は、型エイリアスのルール (後述) によって許されている場合にのみ、安全にアクセスできます。T1
のメンバオブジェクトへのポインタは別のクラス T2
の別のメンバオブジェクトへのポインタに変換できます。 T2
のアライメントが T1
より厳しくなければ、元の型 T1
に変換し戻すと元の値を生成します。 それ以外では結果のポインタを安全に使用することはできません。すべてのキャスト式と同様に、
- new_type が左辺値参照型または関数への右辺値参照型の場合、結果は lvalue です。
- new_type がオブジェクト型への右辺値参照型の場合、結果は xvalue です。
- そうでなければ、結果は prvalue です。
[編集] キーワード
[編集] 型エイリアス
AliasedType
型の glvalue を通して DynamicType
型のオブジェクトの格納されている値の読み書きを試みたとき、以下のいずれかが真でなければ動作は未定義です。
-
AliasedType
とDynamicType
が相似である。 -
AliasedType
がDynamicType
の符号付き版または符号なし版である (cv 修飾されていても構いません)。 -
AliasedType
が std::byte、 (C++17以上) char、 または unsigned char である。 これは任意のオブジェクトのオブジェクト表現をバイトの配列として調べることを許可します。
非形式的には、以下の場合、2つの型は相似です (最上段の cv 修飾は無視します)。
- どちらも同じ型である。
- どちらもポインタであり、その指す先の型が相似である。
- どちらも同じクラスのメンバへのポインタであり、その指す先のメンバの型が相似である。
- どちらも同じサイズの配列またはどちらも境界が未知な配列であり、その配列の要素が相似である。
例えば、
- const int * volatile * と int * * const は相似です。
- const int (* volatile S::* const)[20] と int (* const S::* volatile)[20] は相似です。
- int (* const *)(int *) と int (* volatile *)(int *) は相似です。
- int (S::*)() const と int (S::*)() は相似ではありません。
- int (*)(int *) と int (*)(const int *) は相似ではありません。
- const int (*)(int *) と int (*)(int *) は相似ではありません。
- int (*)(int * const) と int (*)(int *) は相似です (同じ型です)。
- std::pair<int, int> と std::pair<const int, int> は相似ではありません。
このルールにより型エイリアス解析が有効となり、コンパイラはある型の glvalue を通して読み込んだ値が別の型の glvalue への書き込みによって変更されることはない (前述の例外を除いて) と仮定できます。
多くの C++ コンパイラは、 union の非アクティブなメンバを通した間違った型のアクセスを許容するために、非標準の言語拡張としてこのルールを緩和しています (C ではそのようなアクセスは未定義ではありません)。
[編集] ノート
標準の厳密なエイリアスルールを定義している段落は、 C から部分的に引き継いだ2つの追加の条項を含みます。
-
AliasedType
が要素または非静的メンバとして前述の型のいずれかを保持する集成体型または共用体型である (再帰的に、その格納されている共用体の部��集成体の要素および非静的データメンバを含みます)。 -
AliasedType
がDynamicType
の基底クラスである (cv 修飾されていても構いません)。
これらの条項は C++ では発生し得ない状況を説明しており、そのため上の説明からは省いています。 C では、集成体のコピーおよび代入は、その集成体オブジェクトを全体としてアクセスします。 しかし C++ では、それらは常にメンバ関数呼び出しを通して行われ、オブジェクト全体ではなく個々の部分オブジェクトにアクセスします (または、共用体の場合はオブジェクト表現を、すなわち unsigned char を経由して、コピーします)。 core issue 2051 も参照してください。
アライメント要件が満たされると仮定すると、 reinterpret_cast
は、ポインタ相互変換可能なオブジェクトに対処するためのわずかな限定的なケース除き、ポインタの値を変更しません。
struct S1 { int a; } s1; struct S2 { int a; private: int b; } s2; // 標準レイアウトでない。 union U { int a; double b; } u = {0}; int arr[2]; int* p1 = reinterpret_cast<int*>(&s1); // s1.a と s1 はポインタ相互変換可能であるため、 // p1 の値は「s1.a へのポインタ」です。 int* p2 = reinterpret_cast<int*>(&s2); // p2 の値は reinterpret_cast によって変更されず、 // 「s2 へのポインタ」です。 int* p3 = reinterpret_cast<int*>(&u); // p3 の値は「u.a へのポインタ」です。 // u.a と u はポインタ相互変換可能です。 double* p4 = reinterpret_cast<double*>(p3); // p4 の値は「u.b へのポインタ」です。 // どちらも u とポインタ相互変換可能であるため、 // u.a と u.b はポインタ相互変換可能です。 int* p5 = reinterpret_cast<int*>(&arr); // p5 の値は reinterpret_cast によって変更されず、 // 「arr へのポインタ」です。
実際には適切な型のオブジェクトを表していない glvalue (reinterpret_cast
を通して取得したものなど) に対する非静的データメンバまたは非静的メンバ関数を指定したクラスメンバアクセスを行うことは、未定義動作です。
struct S { int x; }; struct T { int x; int f(); }; struct S1 : S {}; // 標準レイアウト。 struct ST : S, T {}; // 標準レイアウトでない。 S s = {}; auto p = reinterpret_cast<T*>(&s); // p の値は「s へのポインタ」です。 auto i = p->x; // クラスメンバアクセス式は未定義動作です。 s は T オブジェクトではありません。 p->x = 1; // 未定義動作。 p->f(); // 未定義動作。 S1 s1 = {}; auto p1 = reinterpret_cast<S*>(&s1); // p1 の値は「s1 の S 部分オブジェクトへのポインタ」です。 auto i = p1->x; // OK。 p1->x = 1; // OK。 ST st = {}; auto p2 = reinterpret_cast<S*>(&st); // p2 の値は「st へのポインタ」です。 auto i = p2->x; // 未定義動作。 p2->x = 1; // 未定義動作。
多くのコンパイラはそのようなケースで「厳密なエイリアス」の警告を発します (論理的には「厳密なエイリアスのルール」と一般的に言われている段落以外の別のルールに対する違反であるとしても)。
厳密なエイリアスとそれに関連するルールの目的は、型ベースのエイリアス解析を有効にすることです。 もし2つの無関係な型へのポインタ (例えば int* と float*) が同時に存在でき、どちらも同じメモリをロードまたはストアするために使用できる、という状況をプログラムが合法的に作れるならば、エイリアス解析は使用できなかったでしょう (this email on SG12 reflector も参照してください)。 そのため、そのような状況を作れそうなあらゆる技法は、未定義動作とせざるを得ません。
オブジェクトのバイト列を異なる型の値として解釈する必要がある場合は、 std::memcpy または std::bit_cast (C++20以上) を使用することができます。
double d = 0.1; std::int64_t n; static_assert(sizeof n == sizeof d); // n = *reinterpret_cast<std::int64_t*>(&d); // 未定義動作。 std::memcpy(&n, &d, sizeof d); // OK。 n = std::bit_cast<std::int64_t>(d); // これでも OK。
[編集] 欠陥報告
以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。
DR | 適用先 | 発行時の動作 | 正しい動作 |
---|---|---|---|
CWG 195 | C++98 | conversion between function pointers and object pointers not allowed | made conditionally-supported |
[編集] 例
reinterpret_cast の使用方法をいくつかデモンストレーションします。
#include <cstdint> #include <cassert> #include <iostream> int f() { return 42; } int main() { int i = 7; // ポインタから整数への変換およびその逆変換。 std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast はエラーです。 std::cout << "The value of &i is 0x" << std::hex << v1 << '\n'; int* p1 = reinterpret_cast<int*>(v1); assert(p1 == &i); // 関数ポインタから別の関数ポインタへの変換およびその逆変換。 void(*fp1)() = reinterpret_cast<void(*)()>(f); // fp1(); // 未定義動作。 int(*fp2)() = reinterpret_cast<int(*)()>(fp1); std::cout << std::dec << fp2() << '\n'; // 安全。 // ポインタを通した型エイリアス。 char* p2 = reinterpret_cast<char*>(&i); if(p2[0] == '\x7') std::cout << "This system is little-endian\n"; else std::cout << "This system is big-endian\n"; // 参照を通した型エイリアス。 reinterpret_cast<unsigned int&>(i) = 42; std::cout << i << '\n'; [[maybe_unused]] const int &const_iref = i; // int &iref = reinterpret_cast<int&>(const_iref); // コンパイルエラー、 const の除去はできません。 int &iref = const_cast<int&>(const_iref); // 代わりに const_cast を使用しなければなりません。 }
出力例:
The value of &i is 0x7fff352c3580 42 This system is little-endian 42
[編集] 関連項目
const_cast 変換 | const を追加または除去します |
static_cast 変換 | 基本的な変換を行います |
dynamic_cast 変換 | チェック付きの多相的な変換を行います |
明示的なキャスト | 型の間の許される変換 |
標準変換 | ある型から別の型への暗黙の変換 |