Sample code:
struct S { int x; };
int func()
{
S s{2};
return (int &)s; // Equivalent to *reinterpret_cast<int *>(&s)
}
I believe this is common and considered acceptable. The standard does guarantee that there is no initial padding in the struct. However this case is not listed in the strict aliasing rule (C++17 [basic.lval]/11):
If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:
- (11.1) the dynamic type of the object,
- (11.2) a cv-qualified version of the dynamic type of the object,
- (11.3) a type similar (as defined in 7.5) to the dynamic type of the object,
- (11.4) a type that is the signed or unsigned type corresponding to the dynamic type of the object,
- (11.5) a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object,
- (11.6) an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),
- (11.7) a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
- (11.8) a char, unsigned char, or std::byte type.
It seems clear that the object s
is having its stored value accessed.
The types listed in the bullet points are the type of the glvalue doing the access, not the type of the object being accessed. In this code the glvalue type is int
which is not an aggregate or union type, ruling out 11.6.
My question is: Is this code correct, and if so, under which of the above bullet points is it allowed?
The behaviour of the cast comes down to [expr.static.cast]/13;
The definition of pointer-interconvertible is:
So in the original code,
s
ands.x
are pointer-interconvertible and it follows that(int &)s
actually designatess.x
.So, in the strict aliasing rule, the object whose stored value is being accessed is
s.x
and nots
and so there is no problem, the code is correct.I think it's in expr.reinterpret.cast#11
[1] This is sometimes referred to as a type pun when the result refers to the same object as the source glvalue
Supporting @M.M's answer about pointer-incovertible:
from cppreference:
versus