【问题标题】:Why do they use reinterpret_cast here?他们为什么在这里使用 reinterpret_cast ?
【发布时间】:2024-01-18 16:45:01
【问题描述】:

以下是 PhysX 示例中的一些代码:

std::vector<PxRigidActor*> actors(nbActors);
scene->getActors(PxActorTypeFlag::eRIGID_DYNAMIC | PxActorTypeFlag::eRIGID_STATIC,
                    reinterpret_cast<PxActor**>(&actors[0]), nbActors);

然后在getActors 函数的代码中,他们像这样使用它:

PxU32 NpScene::getActors(PxActorTypeFlags types, PxActor** buffer, PxU32 bufferSize, PxU32 startIndex=0) const
{
   ...
        if ((types & PxActorTypeFlag::eRIGID_STATIC ) && mRigidActors[i]->is<PxRigidStatic>())
        {
            if (virtualIndex >= startIndex)
                buffer[writeCount++] = mRigidActors[i];
            virtualIndex++;
        }
        else if ((types & PxActorTypeFlag::eRIGID_DYNAMIC) && mRigidActors[i]->is<PxRigidDynamic>())
        {
            if (virtualIndex >= startIndex)
                buffer[writeCount++] = mRigidActors[i];
            virtualIndex++;
        }
   ...
}

mRigidActors 定义为Ps::Array&lt;PxRigidActor*&gt;

继承图如下:

所以,我的问题是:

  1. 听说父类的指针可以指向子类的实例。那么,为什么我们需要任何铸造呢?我试过了,但是不强制转换是不行的。

  2. 在这里使用 reinterpret_cast 是否安全? (我想是的,因为它只是指针转换)

  3. 有没有更好的解决方案?

【问题讨论】:

  • PxRigidActorPxActor 有什么关系?
  • 这基本上是将actors.data() 重新解释为PxActors** 而不是PxRigidActor**。从这个名字听起来,一个是从另一个派生而来的。看起来reinterpret_cast 可能是在这里使用的错误类型,当您看到reinterpret_cast 时通常会出现这种情况。编辑:使用&amp;actors[0] 而不是actors.data()reinterpret_castif / else if 的链似乎正在检查每种具体类型对我来说都是危险信号,向我表明这代码不应该作为一个很好的例子。
  • 我相信 reinterpret_cast 被用来代替 static_cast 因为你不能 static_cast 双指针,因为它们永远不会通过继承相关联。使用显式转换是因为您不能隐式执行 reinterpret_cast
  • @vandench 无论 ABI 是什么,任何编译器都可能在优化过程中破坏这一点。 UB 在静态分析期间可能很明显(它似乎不依赖于运行时信息),因此编译器可以发现它。并且允许编译器假设UB没有发生,因此它可以在控制永远不会到达函数调用的假设下优化函数。例如,如果函数调用在if 后面,编译器可以假定if 的条件始终为false。它现在可能有效,但随时可能中断。
  • @vandench 你不能通过测试来反驳 UB。语言规则不允许这样做,生成的二进制文件与它无关。 UB 是一个抽象概念,将应用于抽象 C++ 机器。严格别名的目的主要是让编译器通过缩小aliasing problem的范围来进行更好的优化。我想垃圾回收也有必要,但是严格别名的存在时间比垃圾回收的允许时间要长得多。

标签: c++ inheritance casting reinterpret-cast


【解决方案1】:

听说父类的指针可以指向子类的实例。那么,为什么我们需要任何铸造呢?我试过了,但是不强制转换是不行的。

存在从PxRigidActor*PxActor* 的隐式转换(派生到基址的指针转换),但PxRigidActor**PxActor** 之间没有这种关系

在这里使用 reinterpret_cast 是否安全? (我想是的,因为它只是指针转换)

强制转换本身并不是不安全的,但取消引用由强制转换创建的指针是未定义的行为。

有没有更好的解决方案?

首先用适当的类型定义actors,即

std::vector<PxActor*> actors(nbActors);
scene->getActors(PxActorTypeFlag::eRIGID_DYNAMIC | PxActorTypeFlag::eRIGID_STATIC, actors.data(), nbActors);

然后您可以根据需要static_cast&lt;PxRigidActor*&gt; actors 的元素。

【讨论】:

    【解决方案2】:

    reinterpret_cast&lt;PxActor**&gt;(&amp;actors[0])
    正在转换向量的第一个元素的地址,而不是转换元素本身。

    此外,被调用函数将指针视为数组。也就是说,它将向量的.data() 转换为不同类型的元素。

    您希望在基类/派生类引用或指针之间导航时使用static_cast。但这突出了一个问题:如果基类实例不在派生类的开头,则转换可能会修改地址! reinterpet_cast 避免了这种情况,只是在不更改值的情况下更改类型......但是如果需要更改这种值,则此代码无论如何都无法正常工作。通过转换“out”参数的地址而不是该地址中的值,代码不知道存储在该槽中的任何内容都需要调整回实际类型。

    由于他将指针投射到指针,static_cast 不会直接在同一个地方工作。这是一个双指针,不遵循D*B* 的规则。它必须写static_cast 作为参考转换,然后取 that 的地址。在我的脑海中,像&amp;static_cast&lt;PxActor*&amp;&gt;(actors.data()) 之类的东西(可能有同样的问题;我必须努力让它发挥作用,可能不是一个单一的表达方式;而且我无意尝试这样做.)

    我的猜测是他将旧版 C 转换为 reinterpet_cast 并没有考虑太多,或者看到只有这个有效(与旧版转换在同一位置)。

    但是为什么?

    代码填充基类指针的连续集合。它接受out 参数而不是返回vector,并且调用者希望将该向量定义为派生类型而不是基类型。通常,将其保留为基类应该没问题,因为行为可能是多态的。

    这是从不同的源集合中复制的,每个分支中的代码都相同。它可能应该是通用的,或者使用访问者模式。这将避免大多数类型转换问题。

    【讨论】:

    • A C cast 是我正在运行的另一件事,但我不敢提及这个... :-) Demo on coliru.
    • 我不确定这是否真的可以编译。看起来不像 PxRigidActor** 可以是 static_castPxActor*&amp;。我不确定您是否可以进行引用,因为std::vector 只为您提供右值指针。
    • &amp;static_cast&lt;PxActor*&amp;&gt;(actors.data()) isn't legal