`reinterpret_cast` 真的有用吗?(这是一个关于IT技术的提问标题)

3
我最近了解到,通过reinterpret_cast转换POD的地址将其解释为不同的POD是未定义行为。那么,如果不能用于其名称建议的内容,reinterpret_cast的潜在用例是什么呢?

1
related / dupe:https://dev59.com/questions/N3RB5IYBdhLWcg3wn4UL - NathanOliver
1
@m88 不,你不应该使用 reinterpret_cast。请使用 static_cast - Konrad Rudolph
1
关于已删除答案评论中的请求,这里是缺陷报告链接:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1839r2.pdf。@463035818不是一个数字。 - François Andrieux
1
这里有一个关于reinterpret_cast的用例列表链接。请注意,其中大多数用例实际上并不实用。例如,将对象转换为其自身类型或仅允许两种类型之间来回转换,但中间值无法使用(例如,可以将A转换为B,但使用B会导致UB,尽管可以将其转换回A)。 - François Andrieux
1
如果标题更加客观一些(例如,“reinterpret_cast 的明确定义用例是什么?”),我认为这个问题不会被关闭。也许重新命名它会使它重新开放。 - François Andrieux
显示剩余9条评论
3个回答

6
有两种情况下我使用了reinterpret_cast
  1. char*强制转换为对象指针或反之,用于序列化或与旧版API通信。在这种情况下,从char*到对象指针的转换在严格意义上仍然是UB(虽然很常见)。实际上你不需要reinterpret_cast,你可以使用memcpy代替;但在特定情况下,强制转换可能会避免复制(但在重新解释字节有效的情况下,memcpy通常也不会产生任何多余的拷贝,编译器足够智能)。

  2. 将指针强制转换为std::uintptr_t或反之,以便在旧版API中进行序列化或执行一些非指针算术运算。这绝对是一种奇怪的方式,在低级代码中也并不经常发生;但考虑这样一种情况,即想要利用在给定平台上指针不使用最高有效位的事实,从而可以将这些位用于存储一些位标志。垃圾收集器实现有时会这样做。如果程序员知道指针始终处于8字节边界上(因此最低三位必须为0),指针的低位有时也可以被利用。

但说实话,我真的记不起来我上次具体、合法地使用reinterpret_cast是什么时候了。肯定已经是很多年前的事了。


我一直认为reinterpret_cast是一种向后续开发人员表达“这可能是未定义行为,但我们必须这样做,因为这是API的工作方式,这里有风险”的方式,以便以一种可搜索的方式呈现。如果是安全的话,你会使用static_cast - Mgetz
@Mgetz 这是一个常见的误解。reinterpret_cast,至少现在的目的绝对不是为了利用UB。你真的不应该依赖于 UB(忽略一些标准缺陷)。你可以依赖于实现定义的行为,但这是根本不同的。reinterpret_cast有助于利用实现定义的行为,而不是UB。但你说得对,历史上UB曾经以更加漫不经心的方式对待,一些平台API直接要求UB。 - Konrad Rudolph
@Mgetz 不是,为什么会更好呢?相反,在这种情况下使用static_cast绝对是更好的选择,因为它表明了转换结果是定义明确、安全的,并且能够准确地完成预期的操作。如果说有什么问题的话,那么reinterpret_cast则会给人留下“我不太清楚我在做什么”的印象。总是优先考虑最窄的可用转换,只有在必要时才转向更宽松的转换。 - Konrad Rudolph
1
强调一下:在static_cast可用的情况下,绝对不要使用reinterpret_cast。即使忽略其他原因,这也容易出错!因为如果你使用static_cast但意外地尝试转换错误的类型,编译器会阻止你。而使用reinterpret_cast时,编译器放弃了,并且无法捕获许多愚蠢的错误,而使用static_cast可以避免这些错误。 - Konrad Rudolph
@FrançoisAndrieux 请仔细阅读...我提到过这个。 - Mgetz
显示剩余4条评论

1
符合 C 和 C++ 标准的实现允许在标准不要求时有意义地扩展 C 或 C++ 的语义。这样做的实现比不这样做的实现更适用于更广泛的任务。在许多情况下,有一致的语法来指定构造是有用的,这些构造将被设计为适用于低级编程任务的实现有意义且一致地处理,即使那些不适用于此类目的的实现会处理它们毫无意义。

0
一个非常常见的用例是当你使用C库函数时,它需要一个不透明的void *参数,并将其转发到回调函数。在两端使用reinterpret_cast,这样可以保持一切正确。

2
你不需要使用reinterpret_cast将对象指针转换为/从void*(你需要它来处理对象,但这是未定义的行为,尽管仍然会被执行)。 - Konrad Rudolph
3
static_cast 起作用了(我会说它更可取吗?) - M.M
1
@M.M 我实际上会说 reinterpret_cast 更可取,尽管你可以使用 static_cast 进行转换。原因是使用 reinterpret_cast 应该总是表示危险。通过 void* 进行转换在我看来始终存在危险点。 - Mgetz
1
将隐式转换为void*是一种static_cast,要撤消隐式转换,您也可以使用static_cast。这是适当的用法。 - JDługosz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接