C++ 参考手册
- C++11
- C++14
- C++17
- C++20
- C++ 编译器支持情况表
- 独立与宿主实现
- C++ 语言
- 变量模板(C++14 起)
- 整数字面量
- 聚合初始化
- 比较运算符
- 默认比较(C++20 起)
- 转义序列
- for 循环
- while 循环
- 用户定义转换
- SFINAE
- 主函数
- ASCII 码表
- 标识符
- 类型
- 内存模型
- 对象
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 类
- 运算符重载
- 模板
- 异常
- 事务性内存
- 占位符类型说明符 (C++11 起)
- decltype 说明符
- 函数声明
- final 说明符 (C++11 起)
- override 说明符(C++11 起)
- 引用声明
- 移动构造函数
- 移动赋值运算符
- 枚举声明
- constexpr 说明符(C++11 起)
- 列表初始化 (C++11 起)
- 构造函数与成员初始化器列表
- using 声明
- nullptr,指针字面量
- 基础类型
- 类型别名,别名模版 (C++11 起)
- 形参包
- 联合体声明
- 字符串字面量
- 用户定义字面量 (C++11 起)
- 属性说明符序列(C++11 起)
- Lambda 表达式 (C++11 起)
- noexcept 说明符 (C++11 起)
- noexcept 运算符 (C++11 起)
- alignof 运算符(C++11 起)
- alignas 说明符 (C++11 起)
- 存储类说明符
- 基于范围的 for 循环 (C++11 起)
- static_assert 声明
- 隐式转换
- 代用运算符表示
- 自增/自减运算符
- 折叠表达式(C++17 起)
- 类模板实参推导(C++17 起)
- 模板形参与模板实参
- if 语句
- inline 说明符
- 结构化绑定声明 (C++17 起)
- switch 语句
- 字符字面量
- 命名空间
- 求值顺序
- 复制消除
- consteval 说明符 (C++20 起)
- constinit 说明符 (C++20 起)
- 协程 (C++20)
- 模块 (C++20 起)
- 约束与概念 (C++20 起)
- new 表达式
- do-while 循环
- continue 语句
- break 语句
- goto 语句
- return 语句
- 动态异常说明
- throw 表达式
- try 块
- 命名空间别名
- 类声明
- cv(const 与 volatile)类型限定符
- 默认初始化
- 值初始化(C++03 起)
- 零初始化
- 复制初始化
- 直接初始化
- 常量初始化
- 引用初始化
- 值类别
- C++ 运算符优先级
- 布尔字面量
- 浮点字面量
- typedef 说明符
- 显式类型转换
- static_cast 转换
- dynamic_cast 转换
- const_cast 转换
- reinterpret_cast 转换
- delete 表达式
- 构造函数与成员初始化器列表
- this 指针
- 访问说明符
- 友元声明
- virtual 函数说明符
- explicit 说明符
- 静态成员
- 默认构造函数
- 复制构造函数
- 复制赋值运算符
- 析构函数
- 类模板
- 函数模板
- 显式(全)模板特化
- 汇编声明
- C++ 的历史
- 作用域
- 生存期
- 定义与单一定义规则(ODR)
- 名字查找
- 有限定的名字查找
- 无限定的名字查找
- 如同规则
- 未定义行为
- 翻译阶段
- 常量表达式
- 赋值运算符
- 算术运算符
- 逻辑运算符
- 成员访问运算符
- 其他运算符
- sizeof 运算符
- typeid 运算符
- 指针声明
- 数组声明
- 语言链接
- 详述类型说明符
- 默认实参
- 变长实参
- 实参依赖查找
- 重载决议
- 重载函数的地址
- 注入类名
- 非静态数据成员
- 非静态成员函数
- 嵌套类
- 派生类
- 空基类优化
- 抽象类
- 位域
- 转换构造函数
- 成员模板
- 模板实参推导
- 部分模板特化
- sizeof... 运算符
- 待决名
- 函数 try 块
- 扩充命名空间 std
- 字母缩写
- RAII
- 三/五/零之法则
- PImpl
- 零开销原则
- 类型
- 隐式转换
- 注释
- C++ 关键词
- 预处理器
- C++ 标准库头文件
- 具名要求
- 功能特性测试 (C++20)
- 工具库
- 类型支持(基本类型、RTTI、类型特性)
- 概念库 (C++20)
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 容器库
- 迭代器库
- 范围库 (C++20)
- 算法库
- 数值库
- 输入/输出库
- 文件系统库
- 本地化库
- 正则表达式库
- 原子操作库
- 线程支持库
- 实验性 C++ 特性
- 有用的资源
- 索引
- std 符号索引
- 协程支持 (C++20)
- C++ 关键词
reinterpret_cast 转换
通过重新解释底层位模式在类型间转换。
语法
reinterpret_cast < 新类型 > ( 表达式 )
|
|||||||||
返回 新类型
类型的值。
解释
与 static_cast 不同,但与 const_cast 类似,reinterpret_cast 表达式不会编译成任何 CPU 指令(除非在整数和指针间转换,或在指针表示依赖其类型的不明架构上)。它纯粹是一个编译时指令,指示编译器将 表达式 视为如同具有 新类型 类型一样处理。
唯有下列转换能用 reinterpret_cast 进行,但若转换会转型走常量性或易变性则亦不允许。
表达式
的相同。(C++11 起)T1*
可转换成指向对象指针类型 cv T2
。这严格等价于 static_cast<cv T2*>(static_cast<cv void*>(表达式))(这意味着,若 T2
的对齐要求不比 T1
的更严格,则指针值不改变,且将结果指针转换回原类型将生成其原值)。任何情况下,只有类型别名化(type aliasing)规则允许(见下文)时,结果指针才可以安全地解引用T1
类型的左值表达式可转换成到另一个类型 T2
的引用。结果是与原左值指代同一对象,但有不同类型的左值或亡值。不创建临时量,不进行复制,不调用构造函数或转换函数。只有类型别名化(type aliasing)规则允许(见下文)时,结果指针才可以安全地解引用T1
的成员对象的指针可转换成指向另一个类 T2
的另一个成员对象的指针。若 T2
的对齐不比 T1
更严格,则转换回原类型 T1
将生成原值,否则不能安全地使用结果指针。同所有转型表达式,结果是:
- 左值,若 new_type 是左值引用或到函数类型的右值引用;
- 亡值,若 new_type 是到对象类型的右值引用;
- 否则为纯右值。
关键词
类型别名化
凡在试图通过 AliasedType
类型的泛左值读或修改类型为 DynamicType
的对象的值时,行为未定义,除非下列之一为真:
-
AliasedType
与DynamicType
相似。 -
AliasedType
是DynamicType
的(可有 cv 限定的)有符号或无符号变体。 -
AliasedType
为 std::byte、 (C++17 起)char 或 unsigned char:这容许将任何对象的对象表示作为一个字节数组加以检验。
非正式地说,忽略顶层 cv 限定性,若两个类型符合下列条件,则它们相似:
- 它们是同一类型;或
- 它们都是指针,且被指向的类型相似;或
- 它们都是指向相同类的成员指针,且被指向的成员类型相似;或
|
(C++20 前) |
|
(C++20 起) |
例如:
- 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> 不相似。
此规则允许进行基于类型的别名分析,即编译器假设通过一个类型的泛左值读取的值,不会被通过不同类型的泛左值的写入所修改(依据上述例外情况)。
注意,许多 C++ 编译器作为非标准语言扩展放松此规则,以允许通过 union 的不活跃成员的进行类型错误的访问(这种访问在 C 中并不是未定义的)。
注解
标准中定义严格别名化规则的段落含有两条额外条例,部分地是从 C 继承而来:
-
AliasedType
为聚合类型或 union 类型,它保有前述各类型之一作为其元素或非静态成员(递归地包含子聚合体的元素和被包含的联合体的非静态数据成员)。 -
AliasedType
为DynamicType
的(可有 cv 限定的)基类。
这些条例所描述的情况不可能出现于 C++,从而从上面的讨论中省略。在 C 中,聚合复制和赋值将聚合体对象作为整体访问。但 C++ 中始终通过成员函数调用进行这种行动,这会访问单独的子对象而非整个对象(或在联合体的情况下,复制对象表示,即经由 unsigned char)。见核心问题 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); // 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
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
DR | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
CWG 195 | C++98 | 不允许函数指针和对象指针间的转换 | 使之为条件性支持 |
示例
演示 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 // 必须用 const_cast 代替:int &iref = const_cast<int&>(const_iref); }
可能的输出:
The value of &i is 0x7fff352c3580 42 This system is little-endian 42
参阅
const_cast 转换 | 添加或移除 const |
static_cast 转换 | 进行基本转换 |
dynamic_cast 转换 | 进行有检查的多态转换 |
显式转型 | 在类型间自由转换 |
标准转换 | 从一个类型到另一类型的隐式转换 |