通过void *而不是使用reinterpret_cast进行转换

时间:2009-12-07 21:36:31

标签: c++ casting void-pointers language-lawyer reinterpret-cast

我正在读一本书,但我发现不应该直接使用reinterpret_cast,而应该与static_cast结合使用* *:

T1 * p1=...
void *pv=p1;
T2 * p2= static_cast<T2*>(pv);

而不是:

T1 * p1=...
T2 * p2= reinterpret_cast<T2*>(p1);

但是,我找不到解释为什么这比直接演员更好。如果有人能给我一个解释或指出我的答案,我将非常感激。

提前致谢

P.S。我知道reinterpret_cast用于什么,但我从未见过以这种方式使用

3 个答案:

答案 0 :(得分:26)

对于允许此类强制转换的类型(例如,如果T1是POD类型而T2unsigned char),那么static_cast的方法是明确定义的按标准。

另一方面,reinterpret_cast完全是实现定义的 - 唯一的保证就是你可以将指针类型转换为任何其他指针类型然后返回,你将获得原始价值;而且,你可以将指针类型强制转换为足以保存指针值的整数类型(根据实现而变化,根本不需要存在),然后将其强制转换,然后你将得到原始值。

更具体地说,我将引用标准的相关部分,突出重要部分:

5.2.10 [expr.reinterpret.cast]:

  

reinterpret_cast执行的映射是实现定义。 [注意:它可能会,也可能不会产生与原始值不同的表示。] ...指向对象的指针可以显式转换为指向不同类型对象的指针。)除了转换类型的右值之外“指向T1的指针”到“指向T2的指针”(其中T1和T2是对象类型,T2的对齐要求不比T1更严格)并返回其原始类型产生原始指针值,这种指针转换的结果是未指定的

这样的事情:

struct pod_t { int x; };
pod_t pod;
char* p = reinterpret_cast<char*>(&pod);
memset(p, 0, sizeof pod);

实际上是未指定的。

解释static_cast工作原因有点棘手。以下是使用static_cast重写的上述代码,我相信这些代码始终按照标准的预期工作:

struct pod_t { int x; };
pod_t pod;
char* p = static_cast<char*>(static_cast<void*>(&pod));
memset(p, 0, sizeof pod);

再次,让我引用标准的各个部分,这些部分一起使我得出结论,上述内容应该是可移植的:

3.9 [basic.types]:

  

对于POD类型T的任何对象(基类子对象除外),无论对象是否保持类型T的有效值,构成对象的基础字节(1.7)都可以复制到数组中char或unsigned char。如果将char或unsigned char数组的内容复制回对象,则该对象应随后保持其原始值。

     

T类型对象的对象表示是由T类型的对象占用的N个unsigned char 对象的序列,其中N等于sizeof(T)。

3.9.2 [basic.compound]:

  

cv-qualified(3.9.3)或cv-unqualified type void*(指向void的指针)的对象可用于指向未知类型的对象。 void*应能够保存任何对象指针。 符合cv资格或cv-nonqualified(3.9.3)void*应具有与cv-qualified或cv-nonqualified char* 相同的表示和对齐要求。

3.10 [basic.lval]:

  

如果程序试图通过以下类型之一以外的左值访问对象的存储值,则行为未定义):

     
      
  • ...
  •   
  • char或unsigned char类型
  •   

4.10 [conv.ptr]:

  

“指向cv T的指针”的右值,其中T是对象类型,可以转换为“指向cv void的指针”类型的右值。将“指向cv T的指针”转换为“指向cv void的指针“指向T类型的对象所在的存储位置的起始位置,就好像该对象是T类型的最派生对象(1.8)(即,不是基类子对象)。

5.2.9 [expr.static.cast]:

  

任何标准转换序列(第4节)的反转,除了左值到右值(4.1),array-topointer(4.2),函数到指针(4.3)和布尔值(4.12)之外,可以使用static_cast显式执行。

[编辑] 另一方面,我们有这个宝石:

9.2 [class.mem] / 17:

  

指向POD结构对象的指针,使用reinterpret_cast进行适当转换,指向其初始成员(或者如果该成员是位字段,则指向它所在的单位),反之亦然。 [注意:可能因此在POD-struct对象中是未命名的填充,但不是在其开头,以实现适当的对齐。 ]

这似乎意味着指针之间的reinterpret_cast以某种方式暗示“相同的地址”。去搞清楚。

答案 1 :(得分:6)

没有丝毫怀疑的意图是两种形式都有明确的定义,但措辞无法捕捉到这一点。

这两种形式都可以在实践中发挥作用。

reinterpret_cast更明确的意图,应该是首选。

答案 2 :(得分:3)

真正的原因是因为C ++如何定义继承,以及成员指针。

使用C,指针几乎只是一个地址,应该如此。在C ++中,由于它的一些特性,它必须更加复杂。

成员指针实际上是一个类的偏移量,所以使用C样式转换它们总是一个灾难。

如果你有多个继承的两个虚拟对象也有一些具体的部分,这也是C风格的灾难。但是,多重继承会导致所有问题,因此您无论如何都不应该使用它。

真的希望你从不首先使用这些案件。此外,如果你投了很多这是另一个迹象,那你就会搞砸你的设计。

我最终投出的唯一时间是C ++决定的区域中的原语是不一样的,但显然它们必须是。对于实际的对象,任何时候你想要投射某些东西,开始质疑你的设计,因为你应该在大多数时间“编程到界面”。当然,您无法更改第三方API的工作方式,因此您并不总是有很多选择。