对于允许进行此类强制转换的类型(例如,如果T1
是 POD 类型并且T2
is unsigned 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【基本类型】:
对于 POD 类型 T 的任何对象(基类子对象除外),无论该对象是否持有类型 T 的有效值,组成该对象的底层字节(1.7)都可以复制到 char 或 unsigned 数组中字符。如果将 char 或 unsigned char 数组的内容复制回对象中,则该对象随后应保留其原始值。
T 类型对象的对象表示是 N 个 unsigned char 的序列objects由 T 类型的对象占用,其中 N 等于 sizeof(T)。
3.9.2[基本化合物]:
cv 限定 (3.9.3) 或 cv 不限定类型的对象void*
(指向 void 的指针),可用于指向未知类型的对象。 Avoid*
应能够保存任何对象指针。简历合格或简历不合格 (3.9.3)void*
应具有与简历合格或简历不合格相同的表示和对齐要求char*
.
3.10[基本.lval]:
如果程序尝试通过以下类型之一以外的左值访问对象的存储值,则行为未定义):
- ...
-
char 或 unsigned char 类型.
4.10[转换指针]:
“指向 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。
[EDIT]另一方面,我们有这个宝石:
9.2[类.mem]/17:
指向 POD 结构对象的指针(使用 reinterpret_cast 进行适当转换)指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然。 [注:可能有所以是 POD 结构对象内的未命名填充,但不是在其开头,这是实现适当对齐所必需的。 ]
这似乎意味着reinterpret_cast
指针之间以某种方式暗示“相同的地址”。去搞清楚。