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

Say I want to cast A* to char* and vice-versa, we have two choices (I mean, many of us think we've two choices, because both seems to work! Hence the confusion!):

struct A
{
    int age;
    char name[128];
};

A a;
char *buffer = static_cast<char*>(static_cast<void*>(&a)); //choice 1
char *buffer = reinterpret_cast<char*>(&a); //choice 2

Both work fine.

//convert back
A *pA = static_cast<A*>(static_cast<void*>(buffer)); //choice 1
A *pA = reinterpret_cast<A*>(buffer); //choice 2

Even this works fine!

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

Some of you might think this topic is a duplicate of the previous topics such as listed at the bottom of this post, but it's not. Those topics discuss only theoretically, but none of them gives even a single example demonstrating why reintepret_cast is really needed, and two static_cast would surely fail. I agree, one static_cast would fail. But how about two?

If the syntax of two chained static_cast looks cumbersome, then we can write a function template to make it more programmer-friendly:

template<class To, class From>
To any_cast(From v)
{
    return static_cast<To>(static_cast<void*>(v));
}

And then we can use this, as:

char *buffer = any_cast<char*>(&a); //choice 1
char *buffer = reinterpret_cast<char*>(&a); //choice 2

//convert back
A *pA = any_cast<A*>(buffer); //choice 1
A *pA = reinterpret_cast<A*>(buffer); //choice 2

Also, see this situation where any_cast can be useful: Proper casting for fstream read and write member functions.

So my question basically is,

  • Why do we have reinterpret_cast in C++?
  • Please show me even a single example where two chained static_cast would surely fail to do the same job?

Answers


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.

You need reinterpret_cast to get a pointer with a hardcoded address (like here):

int* pointer = reinterpret_cast<int*>( 0x1234 );

you might want to have such code to get to some memory-mapped device input-output port.


A concrete example:

char a[4] = "Hi\n";
char* p = &a;

f(reinterpret_cast<char (&)[4]>(p));  // call f after restoring full type
      // ^-- any_cast<> can't do this...

// e.g. given...
template <typename T, int N>   // <=--- can match this function
void f(T (&)[N]) { std::cout << "array size " << N << '\n'; }

Other than the practical reasons that others have given where there is a difference in what they can do it's a good thing to have because its doing a different job.

static_cast is saying please convert data of type X to Y. reinterpret_cast is saying please interpret the data in X as a Y.

It may well be that the underlying operations are the same, and that either would work in many cases. But there is a conceptual difference between saying please convert X into a Y, and saying "yes I know this data is declared as a X but please use it as if it was really a Y".


As far as I can tell your choice 1 (two chained static_cast) is dreaded undefined behaviour. Static cast only guarantees that casting pointer to void * and then back to original pointer works in a way that the resulting pointer from these to conversions still points to the original object. All other conversions are UB. For pointers to objects (instances of the user defined classes) static_cast may alter the pointer value.

For the reinterpret_cast - it only alters the type of the pointer and as far as I know - it never touches the pointer value.

So technically speaking the two choices are not equivalent.

EDIT: For the reference, static_cast is described in section 5.2.9 of current C++0x draft (sorry, don't have C++03 standard, the draft I consider current is n3225.pdf). It describes all allowed conversions, and I guess anything not specifically listed = UB. So it can blow you PC if it chooses to do so.


Using of C Style casting is not safer. It never checks for different types can be mixed together. C++ casts helps you to make sure the type casts are done as per related objects (based on the cast you use). This is the more recommended way to use casts than using the traditional C Style casts that's always harmful.


Look, people, you don't really need reinterpret_cast, static_cast, or even the other two C++ styles casts (dynamic* and const).

Using a C style cast is both shorter and allows you to do everything the four C++-style cast let you do.

anyType someVar = (anyOtherType)otherVar;

So why use the C++-style casts? Readability. Secondly: because the more restrictive casts allow more code safety.

*okay, you might need dynamic


Need Your Help

Chaining pluck() and flatten() with lodash

javascript lodash

This works, but how can I chain it?...