-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_cast
与 static_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_cast
从 Property*
变为 TypedProperty*
,编译器将通过减去 vtable 指针的大小进行调整。如果你reinterpret_cast
没有调整,那么得到的指针就会不正确。
错误消息中的"non-zero offset"告诉您指针有一些不同。唯一的问题是它是否是诸如多重继承之类的东西(问题似乎并非如此)或其他东西。
现在更新了原始问题以显示原因是 TypedProperty
和 Property
中的 none 及其基础 [=44] 中至少有一个虚函数的组合=](es)。请理解,reinterpret_cast
未能完成 static_cast
的工作是一个特定于实现的细节,恰好在几乎所有实现中都匹配。 但是标准答案是这样的reinterpret_cast
总是错的!。您永远无法 reinterpret_cast
将有效的基 class 指针转换为有效的派生 class 指针。
如果 TypedProperty
中没有虚函数,或者基数 class 中至少有一个虚函数,则 reinterpret_cast
可以代替 static_cast
,但是仍然是错误的。这是一个意外发生的未定义行为的例子。永远不要依赖它。
我有这个 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_cast
与 static_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_cast
从 Property*
变为 TypedProperty*
,编译器将通过减去 vtable 指针的大小进行调整。如果你reinterpret_cast
没有调整,那么得到的指针就会不正确。
错误消息中的"non-zero offset"告诉您指针有一些不同。唯一的问题是它是否是诸如多重继承之类的东西(问题似乎并非如此)或其他东西。
现在更新了原始问题以显示原因是 TypedProperty
和 Property
中的 none 及其基础 [=44] 中至少有一个虚函数的组合=](es)。请理解,reinterpret_cast
未能完成 static_cast
的工作是一个特定于实现的细节,恰好在几乎所有实现中都匹配。 但是标准答案是这样的reinterpret_cast
总是错的!。您永远无法 reinterpret_cast
将有效的基 class 指针转换为有效的派生 class 指针。
如果 TypedProperty
中没有虚函数,或者基数 class 中至少有一个虚函数,则 reinterpret_cast
可以代替 static_cast
,但是仍然是错误的。这是一个意外发生的未定义行为的例子。永远不要依赖它。