When to use reinterpret_cast?

2018-12-31 18:03发布

I am little confused with the applicability of reinterpret_cast vs static_cast. From what I have read the general rules are to use static cast when the types can be interpreted at compile time hence the word static. This is the cast the C++ compiler uses internally for implicit casts also.

reinterpret_casts are applicable in two scenarios, convert integer types to pointer types and vice versa or to convert one pointer type to another. The general idea I get is this is unportable and should be avoided.

Where I am a little confused is one usage which I need, I am calling C++ from C and the C code needs to hold on to the C++ object so basically it holds a void*. What cast should be used to convert between the void * and the Class type?

I have seen usage of both static_cast and reinterpret_cast? Though from what I have been reading it appears static is better as the cast can happen at compile time? Though it says to use reinterpret_cast to convert from one pointer type to another?

标签: c++ casting
10条回答
无色无味的生活
2楼-- · 2018-12-31 18:39

The short answer: If you don't know what reinterpret_cast stands for, don't use it. If you will need it in the future, you will know.

Full answer:

Let's consider basic number types.

When you convert for example int(12) to unsigned float (12.0f) your processor needs to invoke some calculations as both numbers has different bit representation. This is what static_cast stands for.

On the other hand, when you call reinterpret_cast the CPU does not invoke any calculations. It just treats a set of bits in the memory like if it had another type. So when you convert int* to float* with this keyword, the new value (after pointer dereferecing) has nothing to do with the old value in mathematical meaning.

Example: It is true that reinterpret_cast is not portable because of one reason - byte order (endianness). But this is often surprisingly the best reason to use it. Let's imagine the example: you have to read binary 32bit number from file, and you know it is big endian. Your code has to be generic and works properly on big endian (e.g. ARM) and little endian (e.g. x86) systems. So you have to check the byte order. It is well-known on compile time so you can write constexpr function:

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

Explanation: the binary representation of x in memory could be 0000'0000'0000'0001 (big) or 0000'0001'0000'0000 (little endian). After reinterpret-casting the byte under p pointer could be respectively 0000'0000 or 0000'0001. If you use static-casting, it will always be 0000'0001, no matter what endianness is being used.

查看更多
何处买醉
3楼-- · 2018-12-31 18:40

First you have some data in a specific type like int here:

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

Then you want to access the same variable as an other type like float: You can decide between

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: it means that the same memory is used as a different type. So you could convert binary representations of floats as int type like above to floats. 0x80000000 is -0 for example (the mantissa and exponent are null but the sign, the msb, is one. This also works for doubles and long doubles.

OPTIMIZE: I think reinterpret_cast would be optimized in many compilers, while the c-casting is made by pointerarithmetic (the value must be copied to the memory, cause pointers couldn't point to cpu- registers).

NOTE: In both cases you should save the casted value in a variable before cast! This macro could help:

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; })
查看更多
其实,你不懂
4楼-- · 2018-12-31 18:44

The meaning of reinterpret_cast is not defined by the C++ standard. Hence, in theory a reinterpret_cast could crash your program. In practice compilers try to do what you expect, which is to interpret the bits of what you are passing in as if they were the type you are casting to. If you know what the compilers you are going to use do with reinterpret_cast you can use it, but to say that it is portable would be lying.

For the case you describe, and pretty much any case where you might consider reinterpret_cast, you can use static_cast or some other alternative instead. Among other things the standard has this to say about what you can expect of static_cast (§5.2.9):

An rvalue of type “pointer to cv void” can be explicitly converted to a pointer to object type. A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.

So for your use case, it seems fairly clear that the standardization committee intended for you to use static_cast.

查看更多
深知你不懂我心
5楼-- · 2018-12-31 18:44

One use of reinterpret_cast is if you want to apply bitwise operations to (IEEE 754) floats. One example of this was the Fast Inverse Square-Root trick:

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

It treats the binary representation of the float as an integer, shifts it right and subtracts it from a constant, thereby halving and negating the exponent. After converting back to a float, it's subjected to a Newton-Raphson iteration to make this approximation more exact:

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;
}

This was originally written in C, so uses C casts, but the analogous C++ cast is the reinterpret_cast.

查看更多
其实,你不懂
6楼-- · 2018-12-31 18:44

You could use reinterprete_cast to check inheritance at compile time.
Look here: Using reinterpret_cast to check inheritance at compile time

查看更多
爱死公子算了
7楼-- · 2018-12-31 18:45

Quick answer: use static_cast if it compiles, otherwise resort to reinterpret_cast.

查看更多
登录 后发表回答