-Wreinterpret-base-class 在 Clang / C++ 中是什么意思?

What does -Wreinterpret-base-class mean in Clang / C++?

我有这个 class 层次结构:

class Base {
  // no virtual stuff
}

class Property : Base {
  // no virtual stuff
}

typedef const Property* PropertyID;

template<typename T>
class TypedProperty : public Property {

  // One virtual method
  virtual bool ValidateValue(T& value) const {  return true; }

  inline const T& GetDefaultValue() const { return m_default_value; }

}

后来就是这样用的:

template<typename T>
const T& DoSomething(PropertyID property) {
  return reinterpret_cast<const TypedProperty<T>*>(property)->GetDefaultValue();
}

Clang 发出此警告:

warning: 'reinterpret_cast' to class 'const TypedProperty *' from its base at non-zero offset 'PropertyID' (aka 'const Property *') behaves differently from 'static_cast' [-Wreinterpret-base-class]

并且 "Fix-it" 在 Xcode 中说:

Use 'static_cast' to adjust the pointer correctly while downcasting.

这是什么意思,尤其是 "from its base at non-zero offset" 部分?

使用 reinterpret_caststatic_cast 相比,实际上会出现什么问题:它只是(正确的 AFAIK)投射指针?我知道传递的对象是 const TypedProperty<T>*.

struct A{ int x; };
struct B:A{};
struct C:A,B{};

这里我们有 3 种类型。 C 的一个实例中包含两个 A 的实例。

这两个实例的地址不同。

重新解释转换将简单地获取地址的指针值,并将其视为指向 C 的指针。 C 的实例只有一个地址,而不是两个,因此两个子对象 A 不能具有该值。

上升也会失败(reinterpret_cast<B*>(pointer_to_c)),但上升时使用显式转换比下降时使用 reinterpret_cast 更愚蠢。

一般来说,reinterpret_cast 只是一个好主意,如果你在另一边做了相反的 reinterpret_cast,或者你用它来 char*原始字节。

除了Yakks的例子,涉及到钻石,可以通过简单的多重继承得到:

struct A { int x; };

struct B { int y; };

struct C : A, B {};

在这种情况下,C中的A和Bobjects不能有相同的地址。一会,一会抵消。基本上,一般来说,重新解释从 child 到 base 的转换是不安全的,您应该进行静态转换。

问题中的信息似乎否定了多重继承的答案。所以 vtable 的答案更有可能。

如果 TypedProperty 至少有一个虚函数而 Property 没有虚函数,你就会遇到所描述的问题。

TypedProperty 对象中的第一件事是 vtable 指针,第二件事是 Property 基 class。如果将 static_castProperty* 变为 TypedProperty*,编译器将通过减去 vtable 指针的大小进行调整。如果你reinterpret_cast没有调整,那么得到的指针就会不正确。

错误消息中的"non-zero offset"告诉您指针有一些不同。唯一的问题是它是否是诸如多重继承之类的东西(问题似乎并非如此)或其他东西。

现在更新了原始问题以显示原因是 TypedPropertyProperty 中的 none 及其基础 [=44] 中至少有一个虚函数的组合=](es)。请理解,reinterpret_cast 未能完成 static_cast 的工作是一个特定于实现的细节,恰好在几乎所有实现中都匹配。 但是标准答案是这样的reinterpret_cast总是错的!。您永远无法 reinterpret_cast 将有效的基 class 指针转换为有效的派生 class 指针。

如果 TypedProperty 中没有虚函数,或者基数 class 中至少有一个虚函数,则 reinterpret_cast 可以代替 static_cast,但是仍然是错误的。这是一个意外发生的未定义行为的例子。永远不要依赖它。