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

sin*_*nek 37 c++ casting void-pointers language-lawyer reinterpret-cast

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

T1 * p1=...
void *pv=p1;
T2 * p2= static_cast<T2*>(pv);
Run Code Online (Sandbox Code Playgroud)

代替:

T1 * p1=...
T2 * p2= reinterpret_cast<T2*>(p1);
Run Code Online (Sandbox Code Playgroud)

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

提前致谢

ps我知道用的是什么reinterpret_cast,但我从未见过以这种方式使用它

Pav*_*aev 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);
Run Code Online (Sandbox Code Playgroud)

实际上是未指定的.

解释为什么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);
Run Code Online (Sandbox Code Playgroud)

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

3.9 [basic.types]:

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

类型T的对象的对象表示是由类型T的对象占据的N个无符号字符对象的序列,其中N等于sizeof(T).

3.9.2 [basic.compound]:

cv-qualified(3.9.3)或cv-unqualified类型void*(指向void的指针)的对象可用于指向未知类型的对象.A void*应该能够保存任何对象指针.符合资格或不合格的(3.9.3)void*应具有与cv合格或cv不合格相同的表示和对齐要求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.1),数组 - 拓扑(4.2),函数到指针(4.3)和布尔(4.12)转换之外,任何标准转换序列(第4节)的反转都可以执行显式使用static_cast.

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

9.2 [class.mem]/17:

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

这似乎意味着reinterpret_cast指针之间某种意味着"相同的地址".去搞清楚.

  • ...但是,请参阅最近的编辑.这是令人费解的,我现在倾向于说,根据我之前评论中的逻辑,"static_cast"和"reinterpret_cast"实际上都保证有效. (4认同)

cur*_*guy 6

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

两种形式都将在实践中发挥作用

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