2

This may well have been asked in some other way before (I would be surprised if its not) but I am struggling to find it if it is.

Given:

#include <iostream>
#include <string>

int main()
{
    int * const pi = new int(1);
    long int * const pl = reinterpret_cast<long int * const>(pi);
    std::cout << "val: " << *pl << std::endl;
    return 0;
}

I get the warning:

<source>: In function 'int main()':
<source>:7:27: warning: type qualifiers ignored on cast result type [-Wignored-qualifiers]
    7 |     long int * const pl = reinterpret_cast<long int * const>(pi);
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ASM generation compiler returned: 0
<source>: In function 'int main()':
<source>:7:27: warning: type qualifiers ignored on cast result type [-Wignored-qualifiers]
    7 |     long int * const pl = reinterpret_cast<long int * const>(pi);
      |                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Execution build compiler returned: 0
Program returned: 0
val: 1

But I am not sure why I get this warning since the re-cast should also be a const pointer - just to a different type. I would like to achieve the same code (which works if ignore the warning), but without the warning.

code_fodder
  • 15,263
  • 17
  • 90
  • 167
  • I'm more curious about the issue about breaking strict aliasing and the undefined behavior you will get from that. Not to mention the problems you will have if `sizeof(long int) != sizeof(int)`. What are you really trying to do? What is the actual problem such a cast is supposed to solve? – Some programmer dude Oct 07 '20 at 15:14
  • Unrelated: the program exhibits UB due to strict aliasing violation at `*pl`. – rustyx Oct 07 '20 at 15:15

1 Answers1

10

long int * const is an immutable pointer to mutable data.

reinterpret_cast returns a temporary object. On most temporary objects (including this one), the immutability of the top-level type is irrelevant.

this:

long int * const pl = reinterpret_cast<long int * const>(pi);

is the same as

long int * const pl = reinterpret_cast<long int *>(pi);

the compiler is warning you about it, because presumably you thought typing const there did something, and you are wrong, it did nothing.


Now, this is not directly related to what you asked, but I would be remiss in not mentioning that:

std::cout << "val: " << *pl << std::endl;

results in your program exhibiting undefined behavior. The pointed to object is not a long int, it is an int, and you are accessing it as an object whose type it is not.

reinterpret_cast is not "treat these bytes as a different type", despite many people treating it like it is. Such operations are almost always undefined behavior under the C++ standard.

The proper way to interpret the bytes of object A as an object of type B (when they are suitably trivial types) is to use something like memcpy.

int * const pi = new int(1);
long int l = 0;
static_assert( sizeof(*pi) == sizeof(l) );
::memcpy( &l, pi, sizeof(l) ); // or l = std::bit_cast<long int>(*pi) in C++20
std::cout << "val: " << l << std::endl;

this is legal, if implementation defined output, as you are free to copy bytes of sufficiently trivial types around, and long int is required to have no trap values.


There is no legal way in C++ to have a chunk of memory be read/written to both as a long int and as an int. The amount of aliasing (treating one type as another) in C++ you are allowed to do is limited, because violating aliasing makes certain really powerful optimizations impossible or impractical.

Many programs ignore this fact and rely on the compiler producing "naive" assembly when you do these kind of operations. They are generating code that exhibits undefined behavior.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • 1
    Or, even better, if you have C++20, you can use `std::bit_cast` for this purpose instead of `memcpy`. – hoz Oct 07 '20 at 15:23
  • great answer - I had not put put much though into the std::cout part - I just added that without even thinking it through - its not relevant though. In the real code we know the location contains 4 bytes+ of data its just one function reads in 32-bit bytes and the pointer is to 16-bit bytes (the example above is not the same types) - but we are happy they copy the right size of data. Removing the const did the trick - thanks very much :) – code_fodder Oct 07 '20 at 15:33
  • 1
    @code_fodder There are extremely limited ways you are allowed to do that. "How much memory is there" is not sufficient. Often relying on only "how much memory is there" will pass your tests, but your program is still exhibiting undefined behavior, and a compiler update or seemingly unrelelated change to an unrelated part of the program could break your code. The problem is less on the `cout` line and more on the `reinterpret_cast` line; but the UB only occurs when you _use_ the result of the `reinterpret_cast` in any non-trivial way. – Yakk - Adam Nevraumont Oct 07 '20 at 15:36
  • @hoz not quite there yet, only at c++17 :o – code_fodder Oct 07 '20 at 16:11
  • @Yakk-AdamNevraumont oh I see. So If I use the result to pass into a copy function (like memcpy) I assume that is a trivial (ok) usage? – code_fodder Oct 07 '20 at 16:12
  • @code_fodder `memcpy` is given special dispensation in the C++ standard, as does directly reading `char`s by casting to `char*` and reading them one at a time. A "copy" function is probably not valid. – Yakk - Adam Nevraumont Oct 07 '20 at 17:12
  • Is a `union` not a legal way to have multiple different types share the same memory? – JHBonarius Oct 07 '20 at 17:38
  • @AyxanHaqverdili In theory, the memory layout of `long int` and `int` can be very different? (in practice, it won't be) – Yakk - Adam Nevraumont Oct 07 '20 at 17:52