reinterpret_cast
转换
通过重新解释底层位模式在类型之间进行转换。
目录 |
[编辑] 语法
reinterpret_cast< 目标类型 >( 表达式 ) |
|||||||||
返回类型为 目标类型 的值。
[编辑] 说明
与 static_cast 不同,但与 const_cast 相似,reinterpret_cast 表达式不会编译成任何 CPU 指令(除非在整数和指针之间转换,或者在指针表示取决于其类型的特殊架构上进行指针之间的转换)。它主要是一个编译时指令,指示编译器将 表达式 视为具有 目标类型。
只有以下转换可以使用 reinterpret_cast 完成,除非此类转换会 去除 const 限定(或 volatile 限定)。
static_cast
或 隐式转换。
4) 任何 std::nullptr_t 类型的值,包括 nullptr,都可以转换为任何整数类型,就好像它是 (void*)0 一样,但没有任何值,即使是 nullptr 也不能转换为 std::nullptr_t:为此,应使用 static_cast。
|
(自 C++11 起) |
T1*
都可以转换为另一个对象指针类型 cv T2*
。这完全等同于 static_cast<cv T2*>(static_cast<cv void*>(表达式))(这意味着如果 T2
的对齐要求不比 T1
严格,则指针的值不会改变,并且将生成的指针转换回其原始类型会产生原始值)。在任何情况下,只有当解引用的值是 类型可访问的 时,才能安全地解引用生成的指针。T1
类型的 左值(直到 C++11)泛左值(自 C++11 起) 表达式可以转换为对另一种类型 T2
的引用。结果是 *reinterpret_cast<T2*>(p) 的结果,其中 p 是指向 “指向 T1
的指针” 类型的指针,指向由 表达式 指定的对象或函数。不会创建临时对象,不会进行复制,不会调用构造函数或转换函数。只有当生成的引用是 类型可访问的 时,才能安全地访问它。dlsym
要求的任何 POSIX 兼容系统上),函数指针可以转换为 void* 或任何其他对象指针,反之亦然。如果实现支持双向转换,则转换为原始类型会产生原始值,否则无法安全地解引用或调用生成的指针。T1
的成员对象的指针转换为指向另一个类 T2
的另一个成员对象的指针。如果 T2
的对齐要求不比 T1
严格,则转换回原始类型 T1
会产生原始值,否则生成的指针不能安全使用。与所有强制转换表达式一样,结果是
- 如果 目标类型 是左值引用类型 或函数类型的右值引用(自 C++11 起),则为左值;
|
(自 C++11 起) |
- 否则为纯右值。
[编辑] 类型别名
[编辑] 类型可访问性
如果类型 T_ref
与以下任何类型相似,则可以通过 T_ref
类型的左值(直到 C++11)泛左值(自 C++11 起)访问动态类型为 T_obj
的对象
- char
- unsigned char
|
(自 C++17 起) |
-
T_obj
- 与
T_obj
对应的有符号或无符号类型
如果程序尝试通过无法访问其类型的左值(直到 C++11)泛左值(自 C++11 起)读取或修改对象的存储值,则行为未定义。
此规则启用基于类型的别名分析,其中编译器假定通过一种类型的泛左值读取的值不会被写入不同类型的泛左值所修改(受上述例外情况的限制)。
请注意,许多 C++ 编译器会放宽此规则,作为非标准语言扩展,允许通过联合体的非活动成员进行错误类型的访问(这种访问在 C 中不是未定义的)。
[编辑] 调用兼容性
如果满足以下任何条件,则类型 T_call
与函数类型 T_func
调用兼容
-
T_call
与T_func
类型相同。
|
(自 C++17 起) |
如果通过其函数类型与被调用函数定义的类型不调用兼容的表达式调用函数,则行为未定义。
[编辑] 注释
假设满足对齐要求,reinterpret_cast 不会更改指针的值,除了少数处理指针可互换对象的有限情况外
struct S1 { int a; } s1; struct S2 { int a; private: int b; } s2; // not standard-layout union U { int a; double b; } u = {0}; int arr[2]; int* p1 = reinterpret_cast<int*>(&s1); // value of p1 is "pointer to s1.a" because // s1.a and s1 are pointer-interconvertible int* p2 = reinterpret_cast<int*>(&s2); // value of p2 is unchanged by reinterpret_cast // and is "pointer to s2". int* p3 = reinterpret_cast<int*>(&u); // value of p3 is "pointer to u.a": // u.a and u are pointer-interconvertible double* p4 = reinterpret_cast<double*>(p3); // value of p4 is "pointer to u.b": u.a and // u.b are pointer-interconvertible because // both are pointer-interconvertible with u int* p5 = reinterpret_cast<int*>(&arr); // value of p5 is unchanged by reinterpret_cast // and is "pointer to arr"
对一个实际上并未指定适当类型对象的泛左值(例如通过 reinterpret_cast 获得的泛左值)执行指定非静态数据成员或非静态成员函数的类成员访问会导致未定义的行为
struct S { int x; }; struct T { int x; int f(); }; struct S1 : S {}; // standard-layout struct ST : S, T {}; // not standard-layout S s = {}; auto p = reinterpret_cast<T*>(&s); // value of p is "pointer to s" auto i = p->x; // class member access expression is undefined behavior; // s is not a T object p->x = 1; // undefined behavior p->f(); // undefined behavior S1 s1 = {}; auto p1 = reinterpret_cast<S*>(&s1); // value of p1 is "pointer to the S subobject of s1" auto i = p1->x; // OK p1->x = 1; // OK ST st = {}; auto p2 = reinterpret_cast<S*>(&st); // value of p2 is "pointer to st" auto i = p2->x; // undefined behavior p2->x = 1; // undefined behavior
许多编译器会在这种情况下发出“严格别名”警告,即使从技术上讲,此类构造违反了除通常称为“严格别名规则”的段落之外的其他内容。
严格别名和相关规则的目的是启用基于类型的别名分析,如果程序可以有效地创建两个指向不相关类型(例如,int* 和 float*)的指针可以同时存在并且都可以用来加载或存储相同内存的情况,则该分析将被破坏(请参阅SG12 反射器上的此电子邮件)。因此,任何看似能够创建这种情况的技术都必然会调用未定义的行为。
当需要将对象的字节解释为不同类型的值时,可以使用 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); // Undefined behavior std::memcpy(&n, &d, sizeof d); // OK n = std::bit_cast<std::int64_t>(d); // also OK
如果实现提供了 std::intptr_t 和/或 std::uintptr_t,则从指向对象类型或 cv void 的指针到这些类型的转换始终是明确定义的。但是,函数指针不保证这一点。 |
(自 C++11 起) |
在 C 中,聚合复制和赋值会将聚合对象作为一个整体进行访问。但在 C++ 中,此类操作始终通过成员函数调用执行,该调用访问单个子对象而不是整个对象(或者,在联合体的情况下,复制对象表示形式,即通过 unsigned char)。
[编辑] 关键字
[编辑] 示例
演示 reinterpret_cast 的一些用法
#include <cassert> #include <cstdint> #include <iostream> int f() { return 42; } int main() { int i = 7; // pointer to integer and back std::uintptr_t v1 = reinterpret_cast<std::uintptr_t>(&i); // static_cast is an error std::cout << "The value of &i is " << std::showbase << std::hex << v1 << '\n'; int* p1 = reinterpret_cast<int*>(v1); assert(p1 == &i); // pointer to function to another and back void(*fp1)() = reinterpret_cast<void(*)()>(f); // fp1(); undefined behavior int(*fp2)() = reinterpret_cast<int(*)()>(fp1); std::cout << std::dec << fp2() << '\n'; // safe // type aliasing through pointer char* p2 = reinterpret_cast<char*>(&i); std::cout << (p2[0] == '\x7' ? "This system is little-endian\n" : "This system is big-endian\n"); // type aliasing through reference reinterpret_cast<unsigned int&>(i) = 42; std::cout << i << '\n'; [[maybe_unused]] const int &const_iref = i; // int &iref = reinterpret_cast<int&>( // const_iref); // compiler error - can't get rid of const // Must use const_cast instead: int &iref = const_cast<int&>(const_iref); }
可能的输出
The value of &i is 0x7fff352c3580 42 This system is little-endian 42
[编辑] 缺陷报告
以下行为更改缺陷报告已追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 发布时的行为 | 正确行为 |
---|---|---|---|
CWG 195 | C++98 | 函数指针之间的转换 和对象指针不允许 |
改为有条件地支持 |
CWG 658 | C++98 | 指针转换的结果未指定 (除了转换回原始类型之外) |
为指针提供的规范 其指向的类型满足 对齐要求 |
CWG 799 | C++98 | 目前尚不清楚哪些身份转换 可以通过 reinterpret_cast 完成 |
已澄清 |
CWG 1268 | C++11 | reinterpret_cast 只能将左值转换为引用类型 | 也允许右值 |
CWG 2780 | C++98 | reinterpret_cast 无法转换 函数左值到其他引用类型 |
允许 |
[编辑] 引用
- C++23 标准 (ISO/IEC 14882:2024)
- 7.6.1.10 重解释转换 [expr.reinterpret.cast]
- C++20 标准 (ISO/IEC 14882:2020)
- 7.6.1.9 重解释转换 [expr.reinterpret.cast]
- C++17 标准 (ISO/IEC 14882:2017)
- 8.2.10 重解释转换 [expr.reinterpret.cast]
- C++14 标准 (ISO/IEC 14882:2014)
- 5.2.10 重解释转换 [expr.reinterpret.cast]
- C++11 标准 (ISO/IEC 14882:2011)
- 5.2.10 重解释转换 [expr.reinterpret.cast]
- C++98 标准 (ISO/IEC 14882:1998)
- 5.2.10 重解释转换 [expr.reinterpret.cast]
- C++03 标准 (ISO/IEC 14882:2003)
- 5.2.10 重解释转换 [expr.reinterpret.cast]
[编辑] 另请参阅
const_cast 转换
|
添加或移除 const |
static_cast 转换
|
执行基本转换 |
dynamic_cast 转换
|
执行已检查的多态转换 |
显式转换 | 类型之间允许的转换 |
标准转换 | 从一种类型到另一种类型的隐式转换 |
(C++20) |
将一种类型的对象表示重新解释为另一种类型的对象表示 (函数模板) |