2014-03-27 63 views
3

这是不确定的行为?通过void cast到基地*

class Derived: public Base //No other bases!!! 
{ 
//... 
}; 

Derived d; 
void *pv = &d; 
Base *b = static_cast<Base*>(pv); 
b->SomeMethod(); 

在一个合理的实现,只要有单继承,一个BaseDerived指针的位将匹配。但语言是否能保证它?

编辑:Real Purpose™是通过C层将指针传递给多态C++对象回C++回调。该对象创建为Derived,但在所述回调中作为Base使用。全文是here

回答

5

关于

“在一个合理的实现,只要有单继承,一个基类和派生指针的位将匹配。”

是简单类型,但没有碱非多态性和衍生引入了一些虚拟构件。

所以,一个转换Derived*void*,然后直接到Base*是未定义的行为不只是在学术上形式上,而且在实践中。


顺带一提,在特殊铸铁dynamic_cast<void*>(p)给你一个指针最派生类对象如果p静态已知类型为指向多态对象。

这对于例如使用像std::unordered_map这样的散列表很有用。


而且在传球,可能与你的真正目的™,标准保证一个指向POD类型的第一个成员都可以转换为指针,类型,反之亦然。这是为了支持C模拟继承。

+0

接受指出一个具体的故障情况。菲利普的回答也可以。 –

+0

Real Purpose™是通过用'void * pv = static_cast (&d);'替换第二行来实现的。在我编写/编译/测试过这个特定情况下,然后开始思考,基类一直有虚拟方法。 –

7

是的,你在做什么事实上调用未定义行为


是从铸造到T*void*T*标准的任务是保证安全,后者T*将持有相同的值作为原始T*

5.29 静态浇铸[expr.static.cast]

...类型的指针的值,以对象转换为 “指向cv空隙” 和背部,可能具有不同的CV-资格,应具有其原始价值。 `。

上述行为保证T * p = static_cast<T*> (static_cast<void*> (some_T_ptr))将在p使得p == some_p得到的值。


在您的例子中,我们从不过铸造void*到与原始T一种类型,但它不是完全相同的T;这意味着操作的结果是未指定的。

编译器是安全的假设,在void*指针(pv)地址原本指向Base因此它不会做任何偏移修改适当调整的事实,我们从Derived*Base*,而不是Base*Base*

编译器可以简单地将pv的值复制到b,因为编译器确实应该调整该值,因为它来自Derived的地址。

Derived * pd = &some_derived; 
void * pv = static_cast<void*> (pd); 
Base * pb = static_cast<Base*> (pv); // unsafe 
+1

“从'T *'投射到'void *'回到'T *'保证是安全的” - 但这不是这里发生的事情。 –

+0

“相当于只是从Derived *'直接转换成'Base *' - 但它相当于'dynamic_cast'(忽略任何指针调整),而不是'static_cast',因此不一定定义良好。 –

+0

@MikeSeymour刚刚意识到这一点,因为我在标准中寻找答案,我会更新我的帖子(包括临时删除)。 –