何时使用reinterpret_cast?

我有点困惑reinterpret_cast vs static_cast的适用性。 从我读过的一般规则来看,当编译时可以解释types的时候,使用静态types转换( static 。 这是C ++编译器内部用于隐式强制转换的强制转换。

reinterpret_cast s适用于两种情况,将整数types转换为指针types,反之亦然,或将一种指针types转换为另一种。 我得到的一般想法是不可移植的,应该避免。

我有点困惑的地方是我需要的一个用法,我从C调用C ++,C代码需要保持C ++对象,所以基本上它保留了一个void* 。 应该使用什么转换来在void *和Classtypes之间进行转换?

我看到static_castreinterpret_cast用法? 虽然从我读过的东西看来, static会更好,因为在编译时可以发生这种情况? 虽然它说使用reinterpret_cast从一个指针types转换到另一个?

C ++标准保证了以下内容:

static_cast一个指针,从void*保留地址。 也就是在下面,a,b和c都指向相同的地址:

 int* a = new int(); void* b = static_cast<void*>(a); int* c = static_cast<int*>(b); 

reinterpret_cast只保证,如果您将一个指针指向一个不同的types, 然后reinterpret_cast它回到原来的types ,你会得到原来的价值。 所以在下面:

 int* a = new int(); void* b = reinterpret_cast<void*>(a); int* c = reinterpret_cast<int*>(b); 

a和c包含相同的值,但b的值未指定。 (在实际中,它通常包含与a和c相同的地址,但在标准中没有说明,在具有更复杂存储系统的机器上可能不是这样)。

对于void和void *, static_cast应该是首选。

reinterpret_cast有必要的一种情况是与不透明数据types进行交互。 这在程序员无法控制的供应商API中经常发生。 以下是供应商提供用于存储和检索任意全局数据的API的一个人为的示例:

 // vendor.hpp typedef struct _Opaque * VendorGlobalUserData; void VendorSetUserData(VendorGlobalUserData p); VendorGlobalUserData VendorGetUserData(); 

要使用这个API,程序员必须将他们的数据转换到VendorGlobalUserData并返回。 static_cast将不起作用,必须使用reinterpret_cast

 // main.cpp #include "vendor.hpp" #include <iostream> using namespace std; struct MyUserData { MyUserData() : m(42) {} int m; }; int main() { MyUserData u; // store global data VendorGlobalUserData d1; // d1 = &u; // compile error // d1 = static_cast<VendorGlobalUserData>(&u); // compile error d1 = reinterpret_cast<VendorGlobalUserData>(&u); // ok VendorSetUserData(d1); // do other stuff... // retrieve global data VendorGlobalUserData d2 = VendorGetUserData(); MyUserData * p = 0; // p = d2; // compile error // p = static_cast<MyUserData *>(d2); // compile error p = reinterpret_cast<MyUserData *>(d2); // ok if (p) { cout << p->m << endl; } return 0; } 

以下是示例API的一个人为实现:

 // vendor.cpp static VendorGlobalUserData g = 0; void VendorSetUserData(VendorGlobalUserData p) { g = p; } VendorGlobalUserData VendorGetUserData() { return g; } 

reinterpret_cast的含义不是由C ++标准定义的。 因此,理论上reinterpret_cast可能会导致程序崩溃。 在实践中,编译器试图做你期望的事情,就是把你所传递的东西当作你所投射的types来解释。 如果你知道你将要使用的编译器与reinterpret_cast做什么,你可以使用它,但是说它是可移植的将是撒谎。

对于你描述的情况,以及几乎任何你可能会考虑reinterpret_cast ,你可以使用static_cast或者其他的替代方法。 除此之外,这个标准对于static_cast (§5.2.9)有什么期望呢?

types“指向cv void的指针”右值可以显式转换为指向对象types的指针。 将指向对象的types指针的值转换为“指向cv void的指针”并返回到原始指针types将具有其原始值。

所以对于你的用例,标准化委员会打算使用static_cast似乎很清楚。

简短的回答:如果您不知道reinterpret_cast代表什么,请不要使用它。 如果将来需要它,你会知道的。

完整答案:

我们来考虑基本的数字types。

当你将int(12)转换为unsigned float (12.0f)处理器需要调用一些计算,因为两个数字都有不同的位表示。 这就是static_cast代表的意思。

另一方面,当你调用reinterpret_cast ,CPU不会调用任何计算。 它只是在内存中处理一些位,就像它有另一种types一样。 所以当你用这个关键字将int*转换为float* ,新的值(在指针取消后)和数值中的旧值无关。

例子: reinterpret_cast确实是不可移植的,因为一个原因 – 字节顺序(字节顺序)。 但是这通常是令人惊讶的使用它的最好理由。 让我们来想象一下这个例子:你必须从文件中读取二进制的32位数字,而且你知道它是大端的。 你的代码必须是通用的,可以在大端(如ARM)和小端(如x86)系统上正常工作。 所以你必须检查字节顺序。 编译时间是众所周知的,所以你可以编写constexpr函数:

 constexpr bool is_little_endian() { unsigned short x=0x0001; auto p = reinterpret_cast<unsigned char*>(&x); return *p != 0; } 

说明:内存中x的二进制表示可以是0000'0000'0000'0001 (大)或0000'0001'0000'0000 (小端)。 重新0000'0000p指针下的字节可分别为0000'00000000'0001 。 如果你使用静态铸造,无论使用什么样的字节顺序,总会是0000'0001

reinterpret_cast的一个用途是如果您想要将位操作应用于(IEEE 754)浮点数。 其中一个例子就是快速逆向平方根技巧:

https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

它将浮点数的二进制表示法看作一个整数,将其右移并将其从常量中减去,从而将指数减半并取反。 在转换回浮点数后,经过牛顿 – 拉夫逊迭代,使这个近似更准确:

 float Q_rsqrt( float number ) { long i; float x2, y; const float threehalfs = 1.5F; x2 = number * 0.5F; y = number; i = * ( long * ) &y; // evil floating point bit level hacking i = 0x5f3759df - ( i >> 1 ); // what the deuce? y = * ( float * ) &i; y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration // y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed return y; } 

这最初是用C语言编写的,所以使用C语言转换,但类似的C ++语言转换是reinterpret_cast。

您可以使用reinterprete_cast在编译时检查inheritance。
看看这里: 使用reinterpret_cast在编译时检查inheritance

 template <class outType, class inType> outType safe_cast(inType pointer) { void* temp = static_cast<void*>(pointer); return static_cast<outType>(temp); } 

我试图用模板来结束和写一个简单的安全演员。 请注意,此解决scheme不保证在函数上投射指针。

首先你有一些特定types的数据,比如int:

 int x = 0x7fffffff://==nan in binary representation 

然后你想访问像float这样的其他types的variables:你可以决定

 float y = reinterpret_cast<float&>(x); //this could only be used in cpp, looks like a function with template-parameters 

要么

 float y = *(float*)&(x); //this could be used in c and cpp 

简单说:这意味着相同的内存被用作不同的types。 所以你可以将浮点数的二进制表示forms转换为像上面那样的inttypes的浮点数。 例如0x80000000是-0(尾数和指数都是空的,但符号,即MSB是1,这也适用于双打和长双打。

优化:我认为reinterpret_cast将在许多编译器中进行优化,而c-cast是由pointerar arithmetic(值必须复制到内存,导致指针不能指向CPU寄存器)。

注意:在这两种情况下,您应该在铸造前将铸造值保存在variables中! 这个macros可以帮助:

 #define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; }) 

快速回答:如果编译时使用static_cast ,否则请使用reinterpret_cast

阅读常见问题 ! 在C中保存C ++数据可能是有风险的。

在C ++中,一个指向一个对象的指针可以被转换为void *而不需要任何强制转换。 但相反的事实并非如此。 你需要一个static_cast来获取原始指针。