-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_cast
与static_cast
实际会出现什么问题:它只是(正确地AFAIK)转换指针?我知道传递的对象是const TypedProperty<T>*
。
【问题讨论】:
实际上可能很少涉及指针运算(想想 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
的实例只有一个地址,而不是两个,所以A
s 的两个子对象都不能有那个值。
向上也将无法工作 (reinterpret_cast<B*>(pointer_to_c)
),但在向上时使用显式强制转换比在向下时使用 reinterpret_cast
更愚蠢。
一般来说,reinterpret_cast
仅当您在另一侧执行相反的 reinterpret_cast
时,或者您将它用于 char*
以获取原始字节时,才在指针上是一个好主意。
【讨论】:
更重要的是,我认为 reinterpret_cast(c) 几乎肯定不会指向 B,而是指向第一个 A。【参考方案2】:问题中的信息似乎否认了多重继承的答案。所以vtable的答案更有可能。
如果TypedProperty
至少有一个虚函数,而Property
没有虚函数,你就会得到所描述的问题。
TypedProperty
对象中的第一件事是 vtable 指针,第二件事是 Property
基类。如果您将static_cast
从Property*
变为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++ 中是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章