c++castingconst-pointer

casting int * const to long int * const warning


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.


Solution

  • 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.