我有这个类层次结构:
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
vs static_cast
会出现什么问题:它只是(正确的AFAIK)投射指针?我知道传递的对象是const TypedProperty<T>*
。
答案 0 :(得分:2)
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*
使用它来获取原始字节
答案 1 :(得分:1)
除了涉及钻石的Yakks示例之外,您还可以通过简单的多重继承来获取它:
struct A { int x; };
struct B { int y; };
struct C : A, B {};
在这种情况下,C中的A和B对象不能具有相同的地址。其中一个将被抵消。基本上,一般来说,从一个孩子到另一个孩子重新解释演员阵容是不安全的,你应该静态演员。
答案 2 :(得分:1)
问题中的信息似乎否定了多重继承的答案。所以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
将代替{{1}},但仍然会出错。这是一个意外工作的未定义行为的例子。不要依赖它。