新C++标准是否完全替代reinterpret_cast的指针及引用转换?
问题背景
reinterpret_cast因易触发未定义行为(UB)、转换意图模糊一直备受争议。C++新标准新增了std::bit_cast、std::span::as_bytes等保留底层对象表示的转换特性,因此需要明确:新代码中,对应标准第7、10、11项的reinterpret_cast用法是否已被替代?
一、标准第7项:对象指针转不同类型对象指针
对象指针可显式转换为不同类型的对象指针。当对象指针类型的纯右值v转换为"指向cv T的指针"类型时,结果等价于
static_cast<cv T*>(static_cast<cv void*>(v))。
已被替代的场景
获取对象的字节指针:
若要将对象指针转为字节类型指针(如const std::byte*),推荐使用std::span::as_bytes或std::span::as_writable_bytes,示例:// 替代 reinterpret_cast<const std::byte*>(&obj) auto byte_ptr = std::span{&obj, 1}.as_bytes().data(); // 可写版本替代 reinterpret_cast<std::byte*>(&obj) auto writable_byte_ptr = std::span{&obj, 1}.as_writable_bytes().data();这种方式明确表达“查看对象字节表示”的意图,且标准保证安全,无需担心严格别名规则的UB。
可平凡复制类型的类型双关:
若要将对象指针转为另一种大小相同、可平凡复制类型的指针,使用std::bit_cast完全替代,示例:struct Foo { int x; }; Foo foo{42}; // 替代 reinterpret_cast<const uint32_t*>(&foo) auto uint_ptr = std::bit_cast<const uint32_t*>(&foo);std::bit_cast是标准明确的安全转换,避免了reinterpret_cast类型双关可能触发的UB。
仍需使用的边缘场景
如果需要进行非平凡类型的指针转换(且不符合严格别名规则的例外情况),或转换后的指针仅用于传递、存储地址(不访问对象),可能仍需使用reinterpret_cast,但此类场景风险极高,需严格确认不会触发UB。
二、标准第10项:成员指针转不同类型成员指针
若T1与T2均为函数类型或均为对象类型,则"指向X的T1类型成员的指针"纯右值可显式转换为不同类型的"指向Y的T2类型成员的指针"纯右值。空成员指针值将转换为目标类型的空成员指针值。转换结果未指定,除非符合以下情况:
- 将"指向成员函数的指针"纯右值转换为另一种指向成员函数的指针类型,再转换回原类型,将得到原指针值。
- 将"指向X的T1类型数据成员的指针"纯右值转换为"指向Y的T2类型数据成员的指针"类型(且T2的对齐要求不严格于T1),再转换回原类型,将得到原指针值。
未被新标准替代,仍需使用reinterpret_cast
目前C++新标准没有针对成员指针跨类型转换的替代特性。如果你的场景符合标准规定的安全条件(转换后可转回原类型,数据成员指针满足对齐要求),比如某些反射、序列化库需要统一处理不同类的成员指针,那么仍然需要使用reinterpret_cast。
使用时必须严格遵守标准限制:
- 成员函数指针转换后,仅当转回原类型时才保证值有效;
- 数据成员指针转换时,目标类型的对齐要求不能比原类型更严格,且转回原类型才保证值有效。
三、标准第11项:左值转不同类型引用(类型双关)
若"指向T1的指针"表达式可通过reinterpret_cast显式转换为"指向T2的指针"类型,则T1类型的左值(指代对象x)可转换为"T2的引用"类型。结果等价于
*reinterpret_cast<T2 *>(p),其中p是指向x的"指向T1的指针"类型指针。不会创建临时对象、执行复制,也不会调用构造函数或转换函数。
已被替代的场景
获取对象的字节引用:
若要获取对象的字节类型引用(如const std::byte&),推荐使用std::span的字节视图,示例:// 替代 reinterpret_cast<const std::byte&>(obj) const std::byte& byte_ref = std::span{&obj, 1}.as_bytes()[0]; // 可写版本替代 reinterpret_cast<std::byte&>(obj) std::byte& writable_byte_ref = std::span{&obj, 1}.as_writable_bytes()[0];可平凡复制类型的引用类型双关:
对于大小相同、可平凡复制的类型,使用std::bit_cast获取引用替代,示例:float f = 3.14f; // 替代 reinterpret_cast<const uint32_t&>(f) auto& uint_ref = std::bit_cast<const uint32_t&>(f);std::bit_cast保证转换的安全性,避免了reinterpret_cast引用转换的UB风险。
仍需使用的边缘场景
如果需要转换的类型不符合std::bit_cast的条件(如大小不同、非平凡类型),但又必须进行类型双关(且符合严格别名规则的例外,如字节类型),可能仍需使用reinterpret_cast,但此类场景必须谨慎验证,确保不触发UB。
内容的提问来源于stack exchange,提问作者Weijun Zhou

