Casting via Void* Instead of Using Reinterpret_Cast

casting via void* instead of using reinterpret_cast

For types for which such cast is permitted (e.g. if T1 is a POD-type and T2 is unsigned char), the approach with static_cast is well-defined by the Standard.

On the other hand, reinterpret_cast is entirely implementation-defined - the only guarantee that you get for it is that you can cast a pointer type to any other pointer type and then back, and you'll get the original value; and also, you can cast a pointer type to an integral type large enough to hold a pointer value (which varies depending on implementation, and needs not exist at all), and then cast it back, and you'll get the original value.

To be more specific, I'll just quote the relevant parts of the Standard, highlighting important parts:

5.2.10[expr.reinterpret.cast]:

The mapping performed by reinterpret_cast is implementation-defined. [Note: it might, or might not, produce a representation different from the original value.] ... A pointer to an object can be explicitly converted to a pointer to an object of different type.) Except that converting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.

So something like this:

struct pod_t { int x; };
pod_t pod;
char* p = reinterpret_cast<char*>(&pod);
memset(p, 0, sizeof pod);

is effectively unspecified.

Explaining why static_cast works is a bit more tricky. Here's the above code rewritten to use static_cast which I believe is guaranteed to always work as intended by the Standard:

struct pod_t { int x; };
pod_t pod;
char* p = static_cast<char*>(static_cast<void*>(&pod));
memset(p, 0, sizeof pod);

Again, let me quote the sections of the Standard that, together, lead me to conclude that the above should be portable:

3.9[basic.types]:

For any object (other than a base-class subobject) of POD type T, whether or not the object holds a valid value of type T, the underlying bytes (1.7) making up the object can be copied into an array of char or unsigned char. If the content of the array of char or unsigned char is copied back into the object, the object shall subsequently hold its original value.

The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T).

3.9.2[basic.compound]:

Objects of cv-qualified (3.9.3) or cv-unqualified type void* (pointer to void), can be used to point to objects of unknown type. A void* shall be able to hold any object pointer. A cv-qualified or cv-unqualified (3.9.3) void* shall have the same representation and alignment requirements as a cv-qualified or cv-unqualified char*.

3.10[basic.lval]:

If a program attempts to access the stored value of an object through an lvalue of other than one of the following types the behavior is undefined):

  • ...
  • a char or unsigned char type.

4.10[conv.ptr]:

An rvalue of type “pointer to cv T,” where T is an object type, can be converted to an rvalue of type “pointer to cv void.” The result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject).

5.2.9[expr.static.cast]:

The inverse of any standard conversion sequence (clause 4), other than the lvalue-to-rvalue (4.1), array-topointer (4.2), function-to-pointer (4.3), and boolean (4.12) conversions, can be performed explicitly using static_cast.

[EDIT] On the other hand, we have this gem:

9.2[class.mem]/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. [Note: There might therefore be unnamed padding within a POD-struct object, but not at its beginning, as necessary to achieve appropriate alignment. ]

which seems to imply that reinterpret_cast between pointers somehow implies "same address". Go figure.

Is it Legal to reinterpret_cast to a void*

It is always legal to convert from a pointer to a type to a pointer to a different type including void, so if T is a type this is legal C++:

T* x;
void *y = reinterpret_cast<void *>(x);

In real world it is never used because void * is a special case, and you obtain the same value with static_cast:

void *y = static_cast<void *>(x); // equivalent to previous reinterpret_cast

(in fact above conversion is implicit and can be simply written void *y = x; - thank to Michael Kenzel for noticing it)

To be more explicit the standard even says in draft n4659 for C++17 8.2.10 Reinterpret cast [expr.reinterpret.cast], §7

When a prvalue v of
object pointer type is converted to the object pointer type “pointer to cv T”, the result is static_cast<cv T*>(static_cast<cv void*>(v)).

When you refer to byte and char being the only legal types, it is just that it is legal to dereference the converted pointer only for those types. void is not included here because you can never dereference a void *.


To specifically answer your question

.. I'm casting from an int** to a void*. And I will eventually cast from the void* back to an int**.

The standard guarantees that first one is a standard (read implicit) conversion:

A prvalue of type “pointer to cv T”, where T is an object type, can be converted to a prvalue of type “pointer
to cv void”. The pointer value (6.9.2) is unchanged by this conversion.

So this is always legal:

int **i = ...;
void *v = i;

For back casting, standard says (in static_cast paragraph):

A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T”,

So this is also legal

int **j = static_cast<int **>(v);

and the standard ensures that j == i.

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.

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.

reinterpret_cast to void* not working with function pointers

reinterpret_cast can't be used to cast a pointer to function to a void*. While there are a few additional things that a C cast can do which aren't allowed by combination of static, reinterpret and const casts, that conversion is not one of them.

In C the cast is allowed, but it's behavior isn't defined (i.e. even round trip isn't guaranteed to work).

Some POSIX functions need the conversion to be well useful.

I've played with several compilers I've here:

  • none prevent the C cast, even in the highest conformance mode. Some give a warning depending on the warning and conformance level, others gave no warning whatever I tried.
  • the reinterpret_cast was a error with some compilers even in the more relaxed level while other accepted it in all case without ever giving a warning.

In the last available draft for C++0X, the reinterpret_cast between function pointers and objects pointers is conditionally supported.

Note that if that make sense or not will depend on the target more than the compiler: a portable compiler like gcc will have a behavior imposed by the target architecture and possibly ABI.

As other have make the remark,

Test* *p(void **a);

defines a function, not a pointer to function. But the function to pointer to function implicit conversion is made for the argument to reinterpret_cast, so what reinterpret_cast get is a Test** (*p)(void** a).

Thanks to Richard which makes me revisit the issue more in depth (for the record, I was mistaken in thinking that the pointer to function to pointer to object was one case where the C cast allowed something not authorized by C++ casts combinations).

Casting a reference to a pointer to a reference to a void*

Is the following well defined behavior?

No, it's not. You can't interpret int * pointer with void * handle, int and void are not similar types. You can convert an int * pointer to void * and back. If your function takes a reference, to do the conversion you need a new temporary variable of type void * to hold the result of the conversion, and then you have to assign it back, like in the other answer https://stackoverflow.com/a/69641609/9072753 .

Anyway, just make it a template, and write nice C++ code with placement new. Something along:

template<typename T>
void reallocate_something(T *&pnt, size_t cnt) {
T *dest = reinterpret_cast<T *>(malloc(cnt * sizeof(T)));
if (dest == NULL) throw ...;
for (size_t i = 0; i < cnt; ++i) {
new (dest[i]) T(pnt[i]);
}
free(static_cast<void*>(pnt));
pnt = dest;
}

Should I use a C++ reinterpret_cast over a C-style cast?

The problem with C-Style casts is that they do a lot under the hood. See here for a detailed explanation: http://anteru.net/2007/12/18/200/

You should try to always use the C++-casts, makes life easier in the long run. The main problem with C-style casts in this case is that you could have written (char*)(&v) while with reinterpret_cast, you would need an additional const_cast, so it's a bit safer. Plus you can easily find reinterpret_cast with a regex, which is not possible for the C-style casts.



Related Topics



Leave a reply



Submit