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?
myStruct.member=23;
invokes UB unlessmember
has a character type, but a compiler would have to be rather obtuse not to recognize such usage. A compiler would likewise have to be obtuse not to recognize situations where a pointer which is freshly converted to a member type is used to access that member. The Standard, however... – Naturalist