reinterpret_cast 変換 - cppreference.com
ベースとなるビットパターンを再解釈することによって型の間で変換します。
構文
reinterpret_cast < new_type > ( expression )
|
|||||||||
new_type 型の値を返します。
説明
static_cast と異なり、しかし const_cast と同様に、 reinterpret_cast 式はいかなる CPU 命令へもコンパイルされません (整数とポインタの間で変換するときやポインタの表現が型によって異なるような変わったアーキテクチャのときを除きます)。 これは純粋に、 expression を new_type 型であるかのように扱うようコンパイラに指示する、コンパイル時の指令です。
以下の変換のみが reinterpret_cast を用いて行うことができます。 ただし cv 修飾を除去することはできません。
1) 整数型、列挙型、ポインタ型、またはメンバへのポインタ型の式は、それ自身の型に変換できます。 結果の値は expression の値と同じです。 (C++11以上)
2) ポインタは、その型のすべての値を保持するために十分大きな任意の整数型に (例えば std::uintptr_t に) 変換できます。
3) 任意の整数型または列挙型の値はポインタ型に変換できます。 十分なサイズの整数型に変換され、その同じポインタ型に逆変換されたポインタは、その元の値を持つことが保証されます。 そうでなければ、結果のポインタは安全に逆参照できません (逆方向の往復変換は保証されません。 同じポインタが複数の整数表現を持つかもしれません)。 ヌルポインタ定数 NULL または整数ゼロがターゲット型のヌルポインタ値を生成することは保証されません。 この目的のためには static_cast または暗黙の変換を使用するべきです。
4) std::nullptr_t 型の任意の値 (nullptr を含む) は、それが (void*)0 であったかのように、任意の整数型に変換できますが、いかなる値も (たとえ nullptr であっても)、 std::nullptr_t に変換することはできません。 この目的のためには static_cast を使用するべきです。 (C++11以上)
5) 任意のオブジェクトポインタ型 T1* は、別のオブジェクトポインタ型 cv T2* に変換できます。 これは static_cast<cv T2*>(static_cast<cv void*>(expression)) とまったく同等です (これは、もし T2 のアライメント要件が T1 より厳しくなければ、ポインタの値は変更されず、結果のポインタのその元の型への逆変換は、その元の値を生成します)。 いかなる場合でも、結果のポインタは、型エイリアスのルール (後述) によって許されている場合にのみ、安全に逆参照できます。
6) T1 型の lvalue 式は、別の型 T2 への参照に変換できます。 結果は、その元の lvalue と同じオブジェクトを参照する lvalue または xvalue ですが、異なる型を持ちます。 一時オブジェクトは作成されず、コピーは行われず、コンストラクタや変換関数は呼ばれません。 結果の参照は、型エイリアスのルール (後述) によって許されている場合にのみ、安全にアクセスできます。
7) 任意の関数へのポインタは、異なる関数型へのポインタに変換できます。 異なる関数型へのポインタを通してその関数を呼ぶことは未定義ですが、そのようなポインタの元の関数型への逆変換は、その元の関数へのポインタを生成します。
8) 処理系によっては (特に、 dlsym によって要求されているように、あらゆる POSIX 互換のシステムでは)、関数ポインタは void* や他の任意のオブジェクトポインタと相互に変換できます。 処理系が両方向の変換をサポートしている場合、元の型への変換は元の値を生成します。 そうでなければ、結果のポインタは安全に逆参照または呼ぶことはできません。
9) 任意のポインタ型のヌルポインタ値は任意の他のポインタ型に変換でき、結果はその型のヌルポインタ値になります。 ヌルポインタ定数 nullptr または std::nullptr_t 型の他のあらゆる値は reinterpret_cast でポインタに変換することはできないことに注意してください。 この目的のためには暗黙の変換または static_cast を使用するべきです。
10) メンバ関数へのポインタは異なる型の異なるメンバ関数へのポインタに変換できます。 元の型に変換し戻すと元の値を生成します。 それ以外では結果のポインタを安全に使用することはできません。
11) 何らかのクラス 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