Lets look at the following code:
int i = 10;
char c = reinterpret_cast<char&>(i);
A glvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result refers to the same object as the source glvalue, but with the specified type.
So the reinterpret_cast<char&>(i)
lvalue with the specified char
type refers to the int
object i
.
To initialize c
, we need value, so the lvalue-to-rvalue conversion is applied [conv.lval]/3.4:
the value contained in the object indicated by the glvalue is the prvalue result.
The result of the L2R conversion is the value contained in the i
object. As long as the value of i
is in the range representable by char
([expr]/4 says that otherwise this is UB), the variable c
shall be initialized to have the same value.
From the implementation POV, on a little-endian platform this is easily achievable by reading a byte at the address of i
object. However, on a big-endian platform the compiler will have to add an offset to fetch the least significant byte. Or, read the whole int
object into a register and mask the first byte, which is acceptable approach on both endians.
If you think that the code above could be easily handled by a compiler to produce a code behaving as required by the C++17 Standard, think of casting a pointer to int
pointing to i
into a pointer to char
. Such cast does not change the pointer value, i.e. it still points to the int
object i
, which means that applying the indirection operator to such pointer with the following L2R conversion shall behave as it was described above, i.e. fetch the value of the int
object if it is representable by the char
type.
In the following code
int i = 10;
f(reinterpret_cast<char*>(&i)); // void f(char*)
should the compiler adjust the address of i
by some offset, if it does not know what the function f
will do with its argument? And also the compiler does not know what will be passed to the function f
. The code above and the function f
are in different translation units.
For example, if f
dereferences the pointer to read the value through it, it shall get the value of the i
, as described above. But it also can be called with a pointer to a real char
object, so f
can't adjust the given pointer. This means that the caller shall adjust the pointer. But what if f
passes the pointer to memcpy
to copy sizeof(int)
bytes to a character array of this size and back to another int
object, as permitted by [basic.types]/3? It is not easy to imagine how to adjust pointers here to mach the required (by both [basic.types]/3 and [conv.lval]/3.4) behavior.
So, what existing implementations do, if there are existing implementations really conforming to the C++17 standard?