命名空间
变体
操作

reinterpret_cast 转换

来自 cppreference.cn
< cpp‎ | language
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句(循环)
for
范围 for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (在 C++11 中弃用*)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
 
 

通过重新解释底层位模式在类型之间转换。

目录

[编辑] 语法

reinterpret_cast< 目标类型 >( 表达式 )

返回类型为 目标类型 的值。

[编辑] 解释

static_cast 不同,但与 const_cast 类似,reinterpret_cast 表达式不会编译成任何 CPU 指令(除非在整数和指针之间,或在指针表示取决于其类型的晦涩架构上进行转换时)。它主要是一个编译时指令,指示编译器将 表达式 视为具有 目标类型 类型。

只有以下转换可以使用 reinterpret_cast 完成,除非此类转换会去除常量性(或易变性)。

1) 整数、枚举、指针或指向成员指针类型的表达式可以转换为其自身类型。结果值与 表达式 的值相同。
2) 指针可以转换为任何足够大的整数类型以容纳其类型的所有值(例如,转换为 std::uintptr_t)。
3) 任何整数或枚举类型的值都可以转换为指针类型。转换为足够大小的整数并返回到相同指针类型的指针保证具有其原始值,否则生成的指针不能安全地解引用(相反方向的往返转换不能保证;相同的指针可能具有多个整数表示形式)。空指针常量 NULL 或整数零不能保证产生目标类型的空指针值;static_cast隐式转换 应该用于此目的。
4) 类型 std::nullptr_t 的任何值,包括 nullptr 都可以转换为任何整数类型,就像它是 (void*)0 一样,但没有任何值,甚至 nullptr 也不能转换为 std::nullptr_tstatic_cast 应该用于此目的。
(自 C++11 起)
5) 任何对象指针类型 T1* 都可以转换为另一个对象指针类型 cv T2*。这完全等同于 static_cast<cv T2*>(static_cast<cv void*>(表达式))(这意味着如果 T2 的对齐要求不比 T1 的严格,则指针的值不会更改,并且将生成的指针转换回其原始类型会产生原始值)。在任何情况下,只有当解引用的值是类型可访问时,才能安全地解引用生成的指针。
6) 类型 T1左值(直到 C++11)泛左值(自 C++11 起) 表达式可以转换为对另一类型 T2 的引用。结果是 *reinterpret_cast<T2*>(p) 的结果,其中 p 是类型为 “指向 T1 的指针” 的指针,指向由 表达式 指定的对象或函数。没有 具体化或(自 C++17 起) 创建临时对象,不进行复制,不调用构造函数或转换函数。只有当生成的引用是类型可访问时,才能安全地访问它。
7) 任何函数指针都可以转换为指向不同函数类型的指针。结果是未指定的,但将这样的指针转换回指向原始函数类型的指针会产生指向原始函数的指针。只有当其函数类型与原始函数类型调用兼容时,才能安全地调用生成的指针。
8) 在某些实现中(特别是在 dlsym 要求的任何 POSIX 兼容系统上),函数指针可以转换为 void* 或任何其他对象指针,反之亦然。如果实现支持双向转换,则转换为原始类型会产生原始值,否则生成的指针不能安全地解引用或调用。
9) 任何指针类型的空指针值都可以转换为任何其他指针类型,从而产生该类型的空指针值。请注意,空指针常量 nullptr 或类型 std::nullptr_t 的任何其他值都不能使用 reinterpret_cast 转换为指针:隐式转换或 static_cast 应该用于此目的。
10) 指向成员函数的指针可以转换为指向不同类型的不同成员函数的指针。转换回原始类型会产生原始值,否则生成的指针不能安全地使用。
11) 指向某个类 T1 的成员对象的指针可以转换为指向另一个类 T2 的另一个成员对象的指针。如果 T2 的对齐方式不比 T1 的严格,则转换回原始类型 T1 会产生原始值,否则生成的指针不能安全地使用。

与所有转型表达式一样,结果是

  • 如果 目标类型 是左值引用类型或指向函数类型的右值引用(自 C++11 起),则为左值;
  • 如果 目标类型 是指向对象类型的右值引用,则为将亡值;
(自 C++11 起)
  • 否则为纯右值。

[编辑] 类型别名

[编辑] 类型可访问性

如果类型 T_ref 与以下任何类型相似,则类型为 动态类型 T_obj 的对象通过类型为 T_ref左值(直到 C++11)泛左值(自 C++11 起)类型可访问的

  • char
  • unsigned char
  • std::byte
(自 C++17 起)
  • T_obj
  • T_obj 对应的有符号或无符号类型

如果程序尝试通过不是类型可访问的 左值(直到 C++11)泛左值(自 C++11 起) 读取或修改对象的存储值,则行为未定义。

此规则启用基于类型的别名分析,其中编译器假定通过一种类型的泛左值读取的值不会被写入不同类型的泛左值(受上述例外情况的约束)修改。

请注意,许多 C++ 编译器放宽了此规则,作为非标准语言扩展,以允许通过 联合体 的非活动成员进行错误类型访问(在 C 中,此类访问不是未定义的)。

[编辑] 调用兼容性

如果满足以下任何条件,则类型 T_call 与函数类型 T_func 调用兼容

  • T_callT_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

[编辑] 示例

演示了 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 无法转换
函数左值到其他引用类型
允许
CWG 2939 C++17 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)
将一种类型的对象表示重新解释为另一种类型的对象表示
(函数模板) [编辑]