8

I was looking at https://en.cppreference.com/w/cpp/language/reinterpret_cast and I noticed that it specifies the legal types we can always cast to:

  • byte*
  • char*
  • unsigned char*

But I did not see void* in the list. Is this an oversight? My use case requires a reinterpret_cast because I'm casting from an int** to a void*. And I will eventually cast from the void* back to an int**.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 2
    "it specifies the legal types we can always cast to" This seems like your invention. That page does not say anything similar to that. – cpplearner Apr 17 '19 at 13:37
  • 1
    You don't need a `reinterpret_cast` to convert a pointer to `void*`. https://godbolt.org/z/-eIfjl – Max Langhof Apr 17 '19 at 13:40
  • I've reopened the question because I didn't see only the top answer applies to this. Leaving a link here: https://stackoverflow.com/questions/573294/when-to-use-reinterpret-cast – NathanOliver Apr 17 '19 at 13:42
  • @cpplearner The types are in the Type Aliasing section. – Jonathan Mee Apr 17 '19 at 13:52
  • @FrançoisAndrieux Ugh, I knew that. I'm just forgetting like an idiot. Could you just post that as an answer and I'll accept? – Jonathan Mee Apr 17 '19 at 13:55
  • @FrançoisAndrieux And I've lied to you. Thank you for posting. I gave you a +1, but [Serge Ballesta answer](https://stackoverflow.com/a/55729272/2642059) felt a little more clear cut to me so I've accepted that one. – Jonathan Mee Apr 18 '19 at 12:32

3 Answers3

5

Those types are exempt from strict aliasing rules. It does not mean they are the only type you can use with reinterpret_cast. In the case of casting an object pointer to another object pointer type, failing to meet the requirements of strict aliasing rules means you cannot safely dereference the result. But you can still cast the resulting pointer back to the original type safely, and use the result as-if it was the original pointer.

The relevant section from cppreference on reinterpret_cast :

(Any object pointer type T1* can be converted to another object pointer type cv T2*. This is exactly equivalent to static_cast<cv T2*>(static_cast<cv void*>(expression)) (which implies that if T2's alignment requirement is not stricter than T1's, the value of the pointer does not change and conversion of the resulting pointer back to its original type yields the original value). In any case, the resulting pointer may only be dereferenced safely if allowed by the type aliasing rules)

When casting back to the original type, AliasedType and DynamicType are the same, so they are similar, which is the first case listed by the aliasing rules where it is legal to dereference the result of reinterpret_cast :

Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:

  • AliasedType and DynamicType are similar.
  • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.
  • AliasedType is std::byte, (since C++17)char, or unsigned char: this permits examination of the object representation of any object as an array of bytes.
Community
  • 1
  • 1
François Andrieux
  • 28,148
  • 6
  • 56
  • 87
4

[expr.reinterpret.cast]/7:

An object pointer can be explicitly converted to an object pointer of a different type.

[basic.compound]/3:

The type of a pointer to cv void or a pointer to an object type is called an object pointer type.

You don't need to use reinterpret_cast, though. Every object pointer type whose pointed type is cv-unqualified is implicitly convertible to void*, and the inverse can be done by static_cast.

cpplearner
  • 13,776
  • 2
  • 47
  • 72
  • Thanks for a really detailed answer... unfortunately this is going a little different direction from my misunderstanding. As you can see from the last line of the question I'm trying to cast from an `int**` to a `void*` which I believe does require a `reinterpret_cast`? – Jonathan Mee Apr 17 '19 at 21:09
  • No? "Pointer to `int*`" _is_ a pointer to cv-unqualified object type. – cpplearner Apr 18 '19 at 05:12
  • Cancel that, it's the cast from the `void*` to the `int**` that requires a `reinterpret_cast`. You agree with that right? I pirated [this answer](https://stackoverflow.com/a/55573563/2642059)'s example to demonstrate: https://ideone.com/G4zuwd Unless you know something I don't I'll need a `reinterpret_cast` to assign `ptr`, right? – Jonathan Mee Apr 18 '19 at 12:25
  • "it's the cast from the void* to the int** that requires a reinterpret_cast" is wrong. All you need is a single static_cast: https://godbolt.org/z/Khvn145G5 – Mike Kaganski Sep 03 '22 at 09:22
2

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.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • You don't even need `static_cast` in your example. Any object pointer type is implicitly convertible to `void*` (with equivalent cv qualifiers). You only need `static_cast` to cast a `void*` back to an object pointer of a particular type… – Michael Kenzel Apr 17 '19 at 14:08
  • Thanks for a really detailed answer... unfortunately this is going a little different direction from my misunderstanding. As you can see from the last line of the question I'm trying to cast from an `int**` to a `void*`, and back, which I believe does require a `reinterpret_cast`? – Jonathan Mee Apr 17 '19 at 21:08