【问题标题】:should I use it or static_cast<void*> then static_cast<myType*> to avoid reinterpret_cast?我应该使用它还是使用 static_cast<void*> 然后使用 static_cast<myType*> 来避免 reinterpret_cast?
【发布时间】:2014-03-01 16:12:19
【问题描述】:

我看到有人建议使用 static_cast&lt;SomeType*&gt;(static_cast&lt;void*&gt;(p)) 而不是重新解释强制转换。

我不明白为什么这样更好,有人可以解释一下吗?

为了论证,这里是一个需要 reinterpret_cast 的示例场景:

DWORD lpNumberOfBytes;
ULONG_PTR lpCompletionKey;
LPOVERLAPPED lpOverlapped;
GetQueuedCompletionStatus(myHandle, &lpNumberOfBytes, &lpCompletionKey, &lpOverlapped, 0);
if(lpCompletionKey == myCustomHandlerKey){
    auto myObject = reinterpret_cast<MyObject*>(lpOverlapped);  //i know this is really a MyObject
}

这是我听到的建议:

auto myObject = static_cast<MyObject*>(static_cast<void*>(lpOverlapped));

编辑:我最初用 在 cmets 部分“asdf”建议在这里使用 static_cast 而不是 reinterpret_cast http://blogs.msdn.com/b/vcblog/archive/2014/02/04/challenge-vulnerable-code.aspx 开始我的问题,但回想起来,我的问题来自那里无关紧要。

【问题讨论】:

  • reinterpret_cast 几乎总是由实现定义,发生的事情并不总是保证是可移植的。
  • @PlasmaHH 这有点误导。 reinterpret_cast 具有非常明确的语义,它们只是非常有限。但是在这种情况下,语义似乎被覆盖了(我没有查看代码就这么说了,基于 OP 的 lpOverlapped 声明)。但阅读 asdf 的评论则完全不同。
  • 当你从一个指针类型转换为另一个指针类型时,reinterpret_cast 在 C++11 中是根据 static_cast&lt;cv ToType*&gt;(static_cast&lt;cv void*&gt;(p)) 指定的。它们都是一样的。
  • 另一方面,如果您想确保转换是安全的,您可以使用something like this,然后使用pointer_cast&lt;MyObject&gt;(lpOverlapped)

标签: c++ casting reinterpret-cast static-cast


【解决方案1】:

§5.2.10 描述了reinterpret_cast 可以执行的合法映射,并指定“不能执行其他转换”。

与您的示例相关的转换是 /7:

指向对象的指针可以显式转换为指向不同对象类型的指针。当“指向T1的指针”类型的prvalue v转换为“指向cv T2的指针”类型时,结果是static_cast&lt;<i>cv T2*&gt;(static_cast&lt;cv</i> void*&gt;(v)) 如果两者都是T1T2 是标准布局类型……并且T2 的对齐要求并不比T1 的对齐要求严格。 [强调我的]

任何other指针转换为对象类型的结果是“未指定”。1

这是reinterpret_cast 危险的两个原因之一:它的转换仅针对指向对象类型的指针子集进行了明确定义,并且编译器通常不提供关于意外误用的诊断。

第二个原因是编译器甚至不会首先检查您尝试执行的映射是否合法,以及将执行许多(语义上完全不同的)映射中的哪一个。

最好明确并告诉编译器(和读者)您想要执行的预期转换是什么。也就是说,asdf 的评论并不完全正确,因为您可能希望通过reinterpret_cast 执行的并非所有 转换都等同于使用static_cast&lt;void*&gt; 后跟static_cast 到目标类型。


1 顺便说一句:简而言之(并略微简化),“standard layout type” 是一种类型(或类型数组),它没有虚函数或混合成员可见性,并且它的所有成员和基地也是标准布局。类型的alignment 是对它可能位于的内存地址的限制。例如,许多机器要求doubles 与可被 8 整除的地址对齐。

【讨论】:

  • +1 用于引用标准和解释最重要的位...如果您这样做并且我可以的话,我会为列出和解释所有个人要求提供 +2。看,例如,即使我忘记了答案中的对齐方式。
  • 何时在定义 reinterptret 和身份双重转换的情况之外定义双重 static_cast
  • @Yakk 我不太确定你的意思,但如果我理解正确的话,双 static_cast via void*` 是限制更多而不是更多一般比reinterpret_cast是偏爱它的原因:它的语义因此更清晰。
  • @KonradRudolph reinterpret_cast 说“如果 条件,那么它等价于 static_cast&lt;T*&gt;(static_cast&lt;void*&gt;(X))” 现在,如果明确写出,双 static_cast 是有效的如果 either condition,或者如果 XT*(即使条件不成立!)。是否有任何其他情况下双重静态转换会导致定义的行为?
  • @Yakk 老实说我不知道​​。在阅读我的答案中引用的部分之前,我实际上会说这种转换是非法的,因为我认为在转换为 void* 之后,唯一合法的转换是回到原始类型。我仍然认为非指针 T 是这样,但现在我不确定了。
【解决方案2】:

asdf 解释得很好,即使在链接的帖子中简洁明了。

因为编译器不知道 CustomImage 派生自 Image at 程序中的这一点。

就我个人而言,我懒得从 msdn 下载垃圾,只是为了深入研究并回答问题。毕竟这是一个编码挑战,你应该弄清楚。

我在 C++ 中的转换规则是:

  1. 使用 C++ 样式转换 xx_cast&lt;T*&gt; 而不是 C 样式 (T*),因为 显式优于隐式。
  2. 只有当你真心实意时才使用重新解释演员表。
  3. 如果您确实使用 reinterpret_cast&lt;T*&gt;,请确保 cast/uncast 是精确的镜像,例如:

.

T* obj = ...;
void* tmp = reinterpret_cast<void*> obj;
T* ref = reinterpret_cast<T*> tmp;  // T* obj --> T* ref

在这里,您必须确保objref 是完全相同的类型,包括 const 限定符、类派生、对齐、内存类型(嵌入),绝对是您能想到的任何东西。

【讨论】:

  • 您能否详细说明“应用/重新应用必须是精确镜像”?你的意思是规范保证我们应该有obj == ref吗?或者您的意思是我们自己应该小心地将reinterpret_cast 仅用于obj == ref 和任何其他用法可能导致未定义或实现定义的行为的类型?
猜你喜欢
  • 2011-05-27
  • 1970-01-01
  • 2015-05-04
  • 2010-09-24
  • 2012-12-28
  • 2011-03-05
  • 1970-01-01
相关资源
最近更新 更多