Why Do We Have Reinterpret_Cast in C++ When Two Chained Static_Cast Can Do Its Job

Why do we have reinterpret_cast in C++ when two chained static_cast can do its job?

There are things that reinterpret_cast can do that no sequence of static_casts can do (all from C++03 5.2.10):

  • A pointer can be explicitly converted to any integral type large enough to hold it.

  • A value of integral type or enumeration type can be explicitly converted to a pointer.

  • A pointer to a function can be explicitly converted to a pointer to a function of a different type.

  • An rvalue of type "pointer to member of X of type T1" can be explicitly converted to an rvalue of type "pointer to member of Y of type T2" if T1 and T2 are both function types or both object types.

Also, from C++03 9.2/17:

  • A pointer to a POD-struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.

Why does reinterpret_cast work in some cases but not others?

Short Answer

The thing is that Base* working = new D2(); is implicitly casting D2* to Base* (static_cast).

So if you have:

D2* d2 = new D2();
Base* b = d2;

You can't be sure that std::addressof(d2) == std::addressof(b) will be true. But reinterpret_cast is only working if std::addressof(d2) == std::addressof(b) is true. So that your code is running correctly is like in the comments mentioned just a fortunate coincidence.



More Detailed

The memory layout for class D2 could look like:

class D2:
0x0000 Attributes of Base
...
0x0010 Attirbutes of D1
...
0x0020 Attributes of D2
...

Base* b = new D2() will save the address of Base (0x0000). Since the attributes of the base classes are always stored before the attributes of the child class, the address stored in b (0x0000) is the same as the address returned by new D2() (0x0000) and reinterpret_cast will work.

But on the other hand the memory layout for class A could look like:

class A:
0x0000 Attributes of HoldData
...
0x0010 Attributes of UserData
...
0x0020 Attributes of Base
...
0x0030 Attributes of A
...

Here the compiler has to store the data of either UserData or Base first. So if UserData gets stored first (like in the example), Base* b = new A() will also save the address of Base (0x0020), but since Base isn't the first stored class in A, the address returned by new A() (0x0000) does not equals the address saved in b (0x0020), since new A() (0x0000) was implicitly statically casted to Base*. This means reinterpret_cast will fail here.

That's why case1 is working and case2 isn't.


One last thing: You should never trust the compiler that it always uses the same memory layout. There are many things concerning the memory layout what is not defined in the standard. Using reinterpret_casthere is undefined behaviour!

Why is it important to use static_cast instead of reinterpret_cast here?

Using static_cast is fine at the example but reinterpret_cast is not. Because reinterpret_cast is not convert vtable.

No, the problem is that the reinterpret_cast is completely oblivious about the inheritance. It will simply return the same address unchanged1. But static_cast knows that you're performing a downcast: i.e. casting from a base class to a derived class. Since it knows both types involved it adjusts the address accordingly, i.e., does the right thing.

Let's pretend our implementation lays out the hypothetical OVERLAPPEDEX class that has a virtual function like this:

+------+------------+------------------+-------------+
| vptr | OVERLAPPED | AssociatedClient | ClientState |
+------+------------+------------------+-------------+
^
|
ptr

The pointer we're given points to the OVERLAPPED subobject. reinterpret_cast would not change that. It would only change the type. Obviously, accessing the OVERLAPPEDEX class through this address would easily wreak havoc, because the locations of its subobjects are all wrong now!

       what we believe we have when we access OVERLAPPEDEX through the pointer
+------+------------+------------------+-------------+
| vptr | OVERLAPPED | AssociatedClient | ClientState |
+------+------+-----+------+-----------+------+------+------+
| vptr | OVERLAPPED | AssociatedClient | ClientState | <- what we actually have
+------+------------+------------------+-------------+
^
|
ptr

static_cast knows that to convert a OVERLAPPED* to OVERLAPPEDEX* it must adjust the address, and does the right thing:

 +------+------------+------------------+-------------+
| vptr | OVERLAPPED | AssociatedClient | ClientState |
+------+------------+------------------+-------------+
^
|
ptr after static_cast

Though, if I use C-Style cast at there(not reinterpret_cast), could it also go wrong?

A C-style cast is defined as the first one of the following that succeeds:

  1. const_cast
  2. static_cast
  3. static_cast, then const_cast
  4. reinterpret_cast
  5. reinterpret_cast, then const_cast

As you can see, a static_cast is tried before reinterpret_cast, so in this case, a C-style cast would also do the right thing.


More info


1Not guaranteed. There are very little guarantees about what happens on a reinterpret_cast. All implementations I know of will simply give out the same address unchanged.

Which cast to use; static_cast or reinterpret_cast?

static_cast provided that you know (by design of your program) that the thing pointed to really is an int.

static_cast is designed to reverse any implicit conversion. You converted to void* implicitly, therefore you can (and should) convert back with static_cast if you know that you really are just reversing an earlier conversion.

With that assumption, nothing is being reinterpreted - void is an incomplete type, meaning that it has no values, so at no point are you interpreting either a stored int value "as void" or a stored "void value" as int. void* is just an ugly way of saying, "I don't know the type, but I'm going to pass the pointer on to someone else who does".

reinterpret_cast if you've omitted details that mean you might actually be reading memory using a type other than the type is was written with, and be aware that your code will have limited portability.

By the way, there are not very many good reasons for using a void* pointer in this way in C++. C-style callback interfaces can often be replaced with either a template function (for anything that resembles the standard function qsort) or a virtual interface (for anything that resembles a registered listener). If your C++ code is using some C API then of course you don't have much choice.

When to use reinterpret_cast?

The C++ standard guarantees the following:

static_casting a pointer to and from void* preserves the address. That is, in the following, a, b and c all point to the same address:

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

reinterpret_cast only guarantees that if you cast a pointer to a different type, and then reinterpret_cast it back to the original type, you get the original value. So in the following:

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

a and c contain the same value, but the value of b is unspecified. (in practice it will typically contain the same address as a and c, but that's not specified in the standard, and it may not be true on machines with more complex memory systems.)

For casting to and from void*, static_cast should be preferred.

static_cast VS reinterpret_cast when casting pointers to pointers

is there any difference between the following expressions?

static_cast      <A*> ( static_cast      <void*> (p) );
reinterpret_cast <A*> ( reinterpret_cast <void*> (p) );

No.

Are the following expressions the same?

static_cast      <B*> ( static_cast      <void*> (p) );
reinterpret_cast <B*> ( reinterpret_cast <void*> (p) );
reinterpret_cast <B*> ( p );

Yes.

Easy way to understand this is to think about how reinterpret_cast from pointer to pointer is specified. reinterpret_cast<T*>(ptr) is specified to behave exactly the same as static_cast<T*>(static_cast<void*>(ptr)) (I've left out cv qualifiers for simplicity).

And of course, static_cast<T>(static_cast<T>(anything)) is equivalent to static_cast<T>(anything) because the outer cast is always an identity conversion.



Can I use B* pointer after this to access b member?

No. If you did that, then the behaviour of the program would be undefined.

Which one to use when static_cast and reinterpret_cast have the same effect?

Everybody has noted that reinterpret_cast<> is more dangerous than static_cast<>.

This is because reinterpret_cast<> ignores all type information and just assigns a new type without any real processing, as a result the processing done is implementation defined (though usually the bit patterns of the pointers are the same).

The thing everybody fails to mention is that reinterpret_cast<> is a means to document your program. It tells somebody reading the code that we had to compromise something and as a result we have ended up with a dangerous cast, and be careful when you mess with this code.

Use the reinterpret_cast<> to highlight these dangerous areas in the code.

When casting from a void* there is not type information for the cast to work with.

So you are either doing an invalid cast or a casting back to the original type that was previously cast into a void*. Any other type of casting is going to end up with some undefined behavior.

This is the perfect situation to use reinterpret_cast<> as the standard guarantees that casting a pointer to void* and back to its original type using reinterpret_cast<> will work. And by using reinterpret_cast<> you are pointing out to the humans that come along afterwords that something bad is happening here.



Related Topics



Leave a reply



Submit