问题描述
我正在读一本书,我发现不应该直接使用 reinterpret_cast
,而是将转换为 void* 与 static_cast
结合使用:
I'm reading a book and I found that reinterpret_cast
should not be used directly, but rather casting to void* in combination with static_cast
:
T1 * p1=...
void *pv=p1;
T2 * p2= static_cast<T2*>(pv);
代替:
T1 * p1=...
T2 * p2= reinterpret_cast<T2*>(p1);
然而,我找不到解释为什么这比直接演员更好.如果有人能给我一个解释或指出我的答案,我将不胜感激.
However, I can't find an explanation why is this better than the direct cast. I would very appreciate if someone can give me an explanation or point me to the answer.
提前致谢
附言我知道 reinterpret_cast
用于什么,但我从未见过以这种方式使用
p.s. I know what is reinterpret_cast
used for, but I never saw that is used in this way
推荐答案
对于允许此类转换的类型(例如,如果 T1
是 POD 类型且 T2
是 unsigned char
),使用 static_cast
的方法在标准中有明确定义.
For types for which such cast is permitted (e.g. if T1
is a POD-type and T2
is unsigned char
), the approach with static_cast
is well-defined by the Standard.
另一方面,reinterpret_cast
完全是实现定义的——你得到的唯一保证是你可以将指针类型转换为任何其他指针类型,然后返回,然后你会得到原始值;而且,您可以将指针类型转换为足够大的整数类型以保存指针值(这取决于实现,并且根本不需要存在),然后将其转换回来,您将获得原始值.
On the other hand, reinterpret_cast
is entirely implementation-defined - the only guarantee that you get for it is that you can cast a pointer type to any other pointer type and then back, and you'll get the original value; and also, you can cast a pointer type to an integral type large enough to hold a pointer value (which varies depending on implementation, and needs not exist at all), and then cast it back, and you'll get the original value.
更具体地说,我将引用标准的相关部分,突出重要部分:
To be more specific, I'll just quote the relevant parts of the Standard, highlighting important parts:
5.2.10[expr.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
,我相信它可以保证始终按照标准的预期工作:
Explaining why static_cast
works is a bit more tricky. Here's the above code rewritten to use static_cast
which I believe is guaranteed to always work as intended by the Standard:
struct pod_t { int x; };
pod_t pod;
char* p = static_cast<char*>(static_cast<void*>(&pod));
memset(p, 0, sizeof pod);
再次,让我引用标准中的部分,这些部分共同使我得出结论,上述内容应该是可移植的:
Again, let me quote the sections of the Standard that, together, lead me to conclude that the above should be portable:
3.9[basic.types]:
3.9[basic.types]:
对于 POD 类型 T 的任何对象(除基类子对象外),无论该对象是否持有 T 类型的有效值,构成该对象的底层字节 (1.7) 都可以复制到字符或无符号字符.如果将 char 或 unsigned char 数组的内容复制回对象,则该对象随后将保持其原始值.
T 类型对象的对象表示是 T 类型对象占用的 N 个 unsigned char 对象 的序列,其中 N 等于 sizeof(T).
The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T).
3.9.2[basic.compound]:
3.9.2[basic.compound]:
cv-qualified (3.9.3) 或 cv-unqualified 类型的对象 void*
(指向 void 的指针),可用于指向未知类型的对象.void*
应该能够保存任何对象指针.cv-qualified 或 cv-unqualified (3.9.3) void*
应与 cv-qualified 或 cv-unqualified char*
具有相同的表示和对齐要求>.
3.10[basic.lval]:
3.10[basic.lval]:
如果程序尝试通过以下类型之一以外的左值访问对象的存储值,则行为未定义):
- ...
- 字符或无符号字符类型.
4.10[conv.ptr]:
4.10[conv.ptr]:
指向 cv T 的指针"类型的右值(其中 T 是对象类型)可以转换为指向 cv void 的指针"类型的右值.将指向 cv T 的指针"转换为指向 cv void 的指针"的结果指向类型 T 的对象所在的存储位置的开头,就好像该对象是类型 T 的最派生对象 (1.8)(即,不是基类子对象).
5.2.9[expr.static.cast]:
5.2.9[expr.static.cast]:
任何标准转换序列(第 4 条)的逆,除了左值到右值 (4.1)、数组到指针 (4.2)、函数到指针 (4.3) 和布尔 (4.12) 转换,可以使用 static_cast 显式执行.
另一方面,我们有这个宝石:
On the other hand, we have this gem:
9.2[class.mem]/17:
9.2[class.mem]/17:
指向 POD 结构对象的指针,使用 reinterpret_cast 进行适当转换,指向其初始成员(或者如果该成员是位域,则指向它所在的单元),反之亦然.[注意:POD 结构对象中可能因此有未命名的填充,但不是在其开头,这是实现适当对齐所必需的.]
这似乎暗示指针之间的 reinterpret_cast
以某种方式暗示相同的地址".去搞清楚.
which seems to imply that reinterpret_cast
between pointers somehow implies "same address". Go figure.
这篇关于通过 void* 进行转换,而不是使用 reinterpret_cast的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!