我对reinterpret_cast和static_cast的适用性有点困惑。根据我所读到的,一般规则是当类型可以在编译时解释时使用静态强制转换,因此是静态这个词。这也是c++编译器内部用于隐式类型转换的类型转换。

reinterpret_cast适用于以下两种情况:

将整数类型转换为指针类型,反之亦然 将一种指针类型转换为另一种。我得到的一般想法是,这是不可移植的,应该避免。

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

我已经看到使用static_cast和reinterpret_cast?虽然从我所读到的似乎静态更好,因为强制转换可以在编译时发生?虽然它说使用reinterpret_cast从一种指针类型转换为另一种?


当前回答

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

指向void*和来自void*的Static_casting指针将保留地址。也就是说,下面的a、b、c都指向同一个地址:

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

Reinterpret_cast只保证如果将一个指针强制转换为不同的类型,然后将其重新转换为原始类型,则得到原始值。下面是:

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。

其他回答

首先你有一些特定类型的数据,比如这里的int:

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

然后你想访问相同的变量作为其他类型,如float: 你可以在

float y = reinterpret_cast<float&>(x);

//this could only be used in cpp, looks like a function with template-parameters

or

float y = *(float*)&(x);

//this could be used in c and cpp

BRIEF:这意味着相同的内存被用作不同的类型。所以你可以像上面那样将浮点数的二进制表示形式转换为int类型。例如,0x80000000为-0(尾数和指数为空,但符号msb为1。这也适用于双打和长双打。

优化:我认为reinterpret_cast会在许多编译器中进行优化,而c-casting是由指针算术进行的(值必须复制到内存中,因为指针不能指向cpu-寄存器)。

注意:在这两种情况下,您都应该在强制转换之前将强制转换的值保存在变量中!这个宏可以帮助:

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

需要reinterpret_cast的一种情况是与不透明数据类型进行接口时。这种情况经常发生在程序员无法控制的供应商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的一个用途是如果你想对(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检查继承。 看这里: 在编译时使用reinterpret_cast检查继承

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

指向void*和来自void*的Static_casting指针将保留地址。也就是说,下面的a、b、c都指向同一个地址:

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

Reinterpret_cast只保证如果将一个指针强制转换为不同的类型,然后将其重新转换为原始类型,则得到原始值。下面是:

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。