通过void *投射,而不是使用reinterpret_cast

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

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

代替:

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

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

提前致谢

ps我知道什么reinterpret_cast用于,但我从来没有看到这是用这种方式

对于允许这种types转换的types(例如,如果T1是PODtypes而T2unsigned char ), static_cast的方法是由标准定义的。

另一方面, reinterpret_cast完全是实现定义的,唯一的保证是你可以将指针types转换为任何其他的指针types,然后返回,你将得到原始值; 还可以将指针types转换为一个足够大的整数types,以保存一个指针值(根据实现的不同而不需要存在),然后将其转换回原来的值。

具体来说,我只是引用标准的相关部分,突出重点部分:

5.2.10 [expr.reinterpret.cast]:

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

所以像这样的东西:

 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]:

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

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

3.9.2 [basic.compound]:

可以使用cv-qualified(3.9.3)或cv-unqualifiedtypesvoid* (void指针)的对象指向未知types的对象。 void*应该能够保存任何对象指针。 一个cv-qualified或cv-unqualified(3.9.3) void*应该具有与cv-qualified或cv-unqualified char*相同的表示和alignment要求

3.10 [basic.lval]:

如果程序试图通过以下types之一的左值访问对象的存储值,则行为是不确定的):

  • 一个字符或无符号的字符types

4.10 [conv.ptr]:

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

5.2.9 [expr.static.cast]:

可以执行除左值到右值(4.1),数组到指针(4.2),函数到指针(4.3)和布尔(4.12)转换之外的任何标准转换序列(第4章)的反转明确使用static_cast。

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

9.2 [class.mem] / 17:

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

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

毫无疑问,这两种forms的意图是明确的,但措辞却没有把握。

这两种forms都将在实践中运作。

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

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

用C,指针几乎就是一个地址,就像它应该是的。 在C ++中,由于其一些特性,它必须更复杂。

成员指针实际上是一个类的偏移量,因此使用C风格来投射它们总是一场灾难。

如果你有多个inheritance了两个虚拟对象,也有一些具体的部分,这也是一个C风格的灾难。 但是在多重inheritance中就是这样,所有这些问题都会导致所有的问题,所以你不应该永远不想使用它。

真的希望你永远不要使用这些情况。 另外,如果你投了很多这是另一个标志,你在你的devise搞乱了。

我唯一的结果是在C ++决定的区域中的原语是不一样的,但是显然它们必须是。 对于实际的对象,任何时候你想投什么东西,开始质疑你的devise,因为你大部分时间应该是“编程接口”。 当然,你不能改变第三方API的工作方式,所以你总是没有多lessselect。