reinterpret_cast 转换
通过重新解释底层位模式在类型间转换。
语法
reinterpret_cast< 目标类型 >( 表达式 )
|
|||||||||
返回目标类型 类型的值。
解释
与 static_cast 不同,但与 const_cast 类似,reinterpret_cast 表达式不会编译成任何 CPU 指令(除非在整数和指针间转换,或在指针表示依赖它的类型的不明架构上)。它纯粹是编译时指令,指示编译器将表达式 视为如同具有目标类型 类型一样处理。
只有下列转换在不移除常量性(或易变性)的场合才能用 reinterpret_cast 执行。
static_cast
或隐式转换。
4) 任何 std::nullptr_t 类型的值,包含 nullptr,都可以转换到任何整数类型,如同它是 (void*)0 一样。但没有值能转换到 std::nullptr_t,甚至 nullptr 也不行:此时应该用 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
的要求),函数指针可以转换成 void* 或任何其他对象指针,反之亦然。如果实现支持双向的转换,那么转换回原类型将生成原值,否则结果指针不能安全地解引用或调用。T1
的成员对象的指针可以转换成指向另一个类 T2
的另一个成员对象的指针。如果 T2
的对齐不比 T1
更严格,那么转换回原类型 T1
将生成原值,否则不能安全地使用结果指针。同所有转换表达式,结果是:
- 左值,如果目标类型 是左值引用类型或到函数类型的右值引用类型 (C++11 起);
|
(C++11 起) |
- 否则是纯右值。
类型别名化
类型可访问性
如果类型 T_ref
与以下类型之一相似,那么动态类型是 T_obj
的对象对 T_ref
类型左值 (C++11 前)泛左值 (C++11 起)具有类型可访问性(即可以通过该值访问对象)。
- 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; // 非标准布局 union U { int a; double b; } u = {0}; int arr[2]; int* p1 = reinterpret_cast<int*>(&s1); // p1 的值是“指向 s1.a 的指针” // 因为 s1.a 与 s1 的指针可以互相转换 int* p2 = reinterpret_cast<int*>(&s2); // reinterpret_cast 不会更改 p2 的值, // 它是“指向 s2 的指针” int* p3 = reinterpret_cast<int*>(&u); // p3 的值是“指向 u.a 的指针”: // u.a 与 u 的指针可以互相转换 double* p4 = reinterpret_cast<double*>(p3); // p4 的指针是“指向 u.b 的指针”: // u.a 与 u.b 的指针可以互相转换, // 因为它们都和 u 指针可以互相转换 int* p5 = reinterpret_cast<int*>(&arr); // reinterpret_cast 不会更改 p5 的值, // 它是“指向 arr 的指针”
在实际上不代表适当类型的对象的泛左值(例如通过 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 的值是“指向 S 的 s1 子对象的指针” 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; // 未定义行为
许多编译器在这种情况下发布“严格别名化”警告,即使在技术上这种构造所违背的并不是称为“严格别名化规则”段落的规则。
严格别名化及其相关规则的目的,是启用基于类型的别名分析,如果程序能合法地创建一种情形,使得两个指向无关类型的指针(例如 int* 和 float*)能同时存在并可以一起用来加载或存储同一内存(见此 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
如果实现提供了 std::intptr_t 和/或 std::uintptr_t,那么从指向对象类型或 cv void 的指针到这些类型的转换始终有良好的定义。然而对于函数指针并没有这种保证。 |
(C++11 起) |
在 C 中,聚合复制和赋值将聚合体对象作为整体访问。但 C++ 中始终通过成员函数调用进行这种操作,这会访问单独的子对象而非整个对象(或在联合体的情况下,复制对象表示,即经由 unsigned char)。这些条例最终经由 CWG 问题 2051 移除。
关键词
示例
演示 reinterpret_cast 的一些用法:
#include <cassert> #include <cstdint> #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 << "&i 的值是 " << std::showbase << 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); std::cout << (p2[0] == '\x7' ? "本系统是小端的\n" : "本系统是大端的\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 // 必须用 const_cast 代替:int &iref = const_cast<int&>(const_iref); }
可能的输出:
&i 的值是 0x7fff352c3580 42 本系统是小端的 42
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
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 Reinterpret cast [expr.reinterpret.cast]
- C++20 标准(ISO/IEC 14882:2020):
- 7.6.1.9 Reinterpret cast [expr.reinterpret.cast]
- C++17 标准(ISO/IEC 14882:2017):
- 8.2.10 Reinterpret cast [expr.reinterpret.cast]
- C++14 标准(ISO/IEC 14882:2014):
- 5.2.10 Reinterpret cast [expr.reinterpret.cast]
- C++11 标准(ISO/IEC 14882:2011):
- 5.2.10 Reinterpret cast [expr.reinterpret.cast]
- C++98 标准(ISO/IEC 14882:1998):
- 5.2.10 Reinterpret cast [expr.reinterpret.cast]
- C++03 标准(ISO/IEC 14882:2003):
- 5.2.10 Reinterpret cast [expr.reinterpret.cast]
参阅
const_cast 转换 | 添加或移除 const |
static_cast 转换
|
进行基本转换 |
dynamic_cast 转换 | 进行有检查的多态转换 |
显式转换 | 在类型间自由转换 |
标准转换 | 从一个类型到另一类型的隐式转换 |
(C++20) |
将一个类型的对象表示重解释为另一类型的对象表示 (函数模板) |