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

Posted

技术标签:

【中文标题】-Wreinterpret-base-class 在 Clang / C++ 中是啥意思?【英文标题】:What does -Wreinterpret-base-class mean in Clang / C++?-Wreinterpret-base-class 在 Clang / C++ 中是什么意思? 【发布时间】:2015-12-22 13:08:37 【问题描述】:

我有这个类层次结构:

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 吐出这个警告:

警告:'reinterpret_cast' 到类 'const TypedProperty *' 从其基类以非零偏移量'PropertyID'(又名'const Property *')的行为不同于'static_cast' [-Wreinterpret-base-class]

Xcode 中的“Fix-it”表示:

在向下转换时使用“static_cast”正确调整指针。

这是什么意思,尤其是“从非零偏移的基数”部分?

使用reinterpret_caststatic_cast 实际会出现什么问题:它只是(正确地AFAIK)转换指针?我知道传递的对象是const TypedProperty&lt;T&gt;*

【问题讨论】:

实际上可能很少涉及指针运算(想想 vtable、多重继承……) @Jarod42:理论上,但不是在这种情况下;也许编译器警告过于悲观? @JohnZwinck:为什么不使用安全的方式?并且 OP 没有显示所有代码(因为他试图调用未声明的 GetDefaultValue() @Jarod42:当然,这里首选 static_cast。我并不是要暗示我认为代码是最优的。 @Pol: small Demo 显示差异。 【参考方案1】:
struct A int x; ;
struct B:A;
struct C:A,B;

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

这两个实例的地址不同。

重新解释转换将简单地获取地址的指针值,并将其视为指向C 的指针。而C 的实例只有一个地址,而不是两个,所以As 的两个子对象都不能有那个值。

向上也将无法工作 (reinterpret_cast&lt;B*&gt;(pointer_to_c)),但在向上时使用显式强制转换比在向下时使用 reinterpret_cast 更愚蠢。

一般来说,reinterpret_cast 仅当您在另一侧执行相反的 reinterpret_cast 时,或者您将它用于 char* 以获取原始字节时,才在指针上是一个好主意。

【讨论】:

更重要的是,我认为 reinterpret_cast(c) 几乎肯定不会指向 B,而是指向第一个 A。【参考方案2】:

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

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

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

错误消息中的“非零偏移”告诉您指针存在一些差异。唯一的问题是它是否是诸如多重继承之类的东西(问题似乎表明并非如此)或其他东西。

现在更新了原始问题以显示原因是TypedProperty 中至少有一个虚函数而Property 及其基类中没有虚函数。请理解,reinterpret_cast 未能完成static_cast 的工作是一个特定于实现的细节,恰好在几乎所有实现中都匹配。 但是标准答案是这样的reinterpret_cast总是错的!。您永远不能reinterpret_cast 将有效的基类指针转换为有效的派生类指针。

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

【讨论】:

TypedProperty 中确实有一个虚拟方法,我在我的问题中忘记提及了。现在更新了。【参考方案3】:

除了 Yakks 的例子,它涉及到一个菱形,你可以通过简单的多重继承来获得它:

struct A  int x; ;

struct B  int y; ;

struct C : A, B ;

在这种情况下,C 中的 A 和 B 对象不能具有相同的地址。其中一个将,一个将被抵消。基本上,一般来说,重新解释从孩子到基地的演员表是不安全的,你应该静态演员表。

【讨论】:

以上是关于-Wreinterpret-base-class 在 Clang / C++ 中是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章