Suppose i have two structs
struct B
{
int n;
};
struct C
{
int k;
};
and
B b = {};
C& c = reinterpret_cast<C&>(b); //Not Ok , compiler(gcc 8.5 with -O2 -Wall) is not happy
C *c1 = reinterpret_cast<C*>(&b); //Okay, compiler(gcc 8.5 with -O2 -Wall) is happy
sample code:
#include<new>
#include<iostream>
int main() {
struct B { int n; };
struct C
{ int k; }
;
B b = {};
C c = reinterpret_cast<C&>(b);
C *c1 = reinterpret_cast<C*>(&b);
printf("b.n is %d\n",b.n);
printf("c.k is %d\n",c.k);
printf("c1.k is %d\n",c1->k);
return 0;
}
Can somebody help me to understand why there is difference in behavior for above code though I believe they are functionally same?
I expected compiler to be happy even on references as I know both types have same memory alignment.
I get the following warning
<source>:142:29: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
C c = reinterpret_cast<C&>(b);
The bullet for reinterpret_cast
that applies here is:
- 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 (see below)
and
- An lvalue (until C++11) glvalue (since C++11) expression of type T1 can be converted to reference to another type T2. The result is that of
*reinterpret_cast<T2*>(p)
, where p is a pointer of type “pointer to T1” to the object designated by expression. No temporary is created, no copy is made, no constructors or conversion functions are called. The resulting reference can only be accessed safely if allowed by the type aliasing rules (see below)
The type aliasing rule says
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:
The only bullet that might apply is:
- AliasedType and DynamicType are similar.
And similar is one of the following:
- they are the same type; or
- they are both pointers, and the pointed-to types are similar; or
- they are both pointers to member of the same class, and the types of the pointed-to members are similar; or
- they are both arrays of the same size or both arrays of unknown bound, and the array element types are similar. (until C++20)
- they are both arrays of the same size or at least one of them is array of unknown bound, and the array element types are similar.
A
and C
are not arrays. A*
and C*
are not similar.
Ergo, both c
and c1
are pretty useless. c
cannot be accessed and c1
can only be cast back to a A*
. Anything else results in undefined behavior.
The compiler is not mandated to warn or error on undefined behavior. It warns for the reference because there is nothing you can do with the reference. It does not warn for the pointer, because such cast can be useful (but only as intermediate result, only to cast back to B*
).
reinterpret_cast
is not a any-to-any cast that you can apply just because A
and C
look the same. If you want to convert a B
to a C
you need a proper conversion.