当前位置:  开发笔记 > 编程语言 > 正文

何时使用reinterpret_cast?

如何解决《何时使用reinterpret_cast?》经验,为你挑选了5个好方法。

我对reinterpret_castvs 的适用性感到困惑static_cast.从我读到的一般规则是使用静态转换,当类型可以在编译时解释,因此这个词static.这是C++编译器内部用于隐式转换的转换.

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

我有点困惑的地方是我需要的一种用法,我从C调用C++并且C代码需要保持C++对象,所以基本上它拥有一个void*.什么演员应该用于在void *类型和类型之间进行转换?

我看过两者的用法static_castreinterpret_cast?虽然从我读过的内容看起来似乎static更好,因为演员阵容可以在编译时发生?虽然它说用于reinterpret_cast从一种指针类型转换为另一种指针类型?



1> jalf..:

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

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

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

a只保证如果您将指针转换为其他类型,然后b返回原始类型,您将获得原始值.所以在下面:

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

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

对于往返于*的铸造,c应该是首选.


这实际上没有回答"何时使用reinterpret_cast"的问题.
当使用`reinterpret_cast`时,在C++ 11中不再指定`b`的值.在C++ 03中,使用`reinterpret_cast`禁止使用`int*`转换为`void*`(虽然编译器没有实现它,但这是不切实际的,因此对C++ 11进行了更改).
我喜欢"b"未定义的事实.它阻止你用它做傻事.如果你将某些东西投射到另一个指针类型,那么你就会遇到问题而且你不能依赖它会让你更加小心.如果您之前使用过static_cast <>,那么'b'的用途是什么?
@LokiAstari我认为未指明并不能阻止你做傻事.当你记得它没有说明时,它只能阻止你.巨大的差异.我个人不喜欢未指定.记得太多了.
我认为reinterpret_cast <>保证了相同的位模式.(与指向另一种类型的有效指针不同).

2> jwfearn..:

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 
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(&u);       // compile error
    d1 = reinterpret_cast(&u);  // ok
    VendorSetUserData(d1);

        // do other stuff...

        // retrieve global data
    VendorGlobalUserData d2 = VendorGetUserData();
    MyUserData * p = 0;
//  p = d2;                                           // compile error
//  p = static_cast(d2);                // compile error
    p = reinterpret_cast(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; }


@Xeo它们不使用void*因为它们在编译时丢失(某些)类型检查.
这可能是一个迟到的问题,但为什么供应商API不使用`void*`呢?
是的,这是我能想到的reinterpret_cast的唯一有意义的用法.
"不透明"数据类型的实际用例是,当您希望将API公开给C但是用C++编写实现时.ICU是一个在几个地方执行此操作的库的示例.例如,在欺骗检查器API中,您处理类型为"USpoofChecker*"的指针,其中`USpoofChecker`是一个空结构.然而,在引擎盖下,无论何时传递一个`USpoofChecker*',它都会经历`reinterpret_cast`到内部C++类型.

3> jaskmar..:

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

完整答案:

我们来考虑基本的数字类型.

例如int(12),当您转换为unsigned float (12.0f)处理器时,需要调用某些计算,因为两个数字都有不同的位表示.这就是static_cast代表.

另一方面,当你调用reinterpret_castCPU时不会调用任何计算.它只是处理内存中的一组位,就像它有另一种类型一样.所以,当你转换int*float*与此关键字,新的值(指针dereferecing之后)无关,在数学意义上的旧值.

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

/*constexpr*/ bool is_little_endian() {
  std::uint16_t x=0x0001;
  auto p = reinterpret_cast(&x);
  return *p != 0;
}

说明:x内存中的二进制表示可以是0000'0000'0000'0001(大)或0000'0001'0000'0000(小端).重新解释后,p指针下的字节可以分别为0000'00000000'0001.如果使用静态转换,0000'0001无论使用什么字节顺序,它都将始终如此.


更具体地说,https://en.cppreference.com/w/cpp/language/constant_expression(项目16)明确指出,reinterpret_cast不能在常量表达式中使用。另请参阅https://github.com/cplusplus/draft/blob/master/papers/N3797.pdf(5.19常数表达式)第125-126页,其中明确排除了reinterpret_cast。然后* 7.1.5 constexpr说明符*项目5(第146页)*对于非模板,非默认constexpr函数...如果不存在参数值,则...可以是核心常量表达式的求值子表达式(5.19),**程序格式错误***

4> flodin..:

其含义reinterpret_cast不是由C++标准定义的.因此,理论上reinterpret_cast可能会导致程序崩溃.在实践中,编译器会尝试按照您的期望进行操作,即解释您传入的内容,就好像它们是您要投射的类型一样.如果你知道你将要使用的编译器reinterpret_cast 可以使用它,但是说它是可移植的就是撒谎.

对于您描述的情况,以及您可能考虑的任何情况reinterpret_cast,您可以使用static_cast或替代其他替代方案.除此之外,标准还有关于你可以期待什么的说法static_cast(§5.2.9):

"指向cv void的指针"类型的右值可以显式转换为指向对象类型的指针.转换为"指向cv void的指针"并返回原始指针类型的对象的类型指针值将具有其原始值.

因此,对于您的用例,标准化委员会似乎相当清楚,您打算使用它static_cast.


大声笑,我怀疑reinterpret_crash可能确实会崩溃你的程序.但是reinterpret_cast不会.;)
@paercebal`template T reinterpret_crash(U a){return*(T*)nullptr; }`
不是你的程序崩溃.该标准提供了一些关于reinterpret_cast的保证.只是没有人们通常期望的那么多.
我在我的编译器上尝试过,不知何故,它拒绝编译`reinterpret_crash`.编译器错误绝不会阻止我崩溃我的重新解释程序.我会尽快报告错误!

5> Adam P. Gouc..:

reinterpret_cast的一个用途是,如果要将按位运算应用于(IEEE 754)浮点数.其中一个例子是Fast Inverse Square-Root技巧:

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

它将float的二进制表示形式视为整数,将其向右移动并从常量中减去它,从而将指数减半和否定.转换回浮点数后,它会进行Newton-Raphson迭代,以使此逼近更精确:

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.

推荐阅读
谢谢巷议
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有