The Standard tells us1 in an example2 than the following is legal:
struct T1 { int a, b; };
struct T2 { int c; double d; };
union U { T1 t1; T2 t2; };
int f() {
U u = { { 1, 2 } }; // active member is t1
return u.t2.c; // OK, as if u.t1.a were nominated
}
Now, adding an indirection level (member function) and removing another (union), I wonder:
Is the following well-defined?
struct T1 { int a; int value() { return a; }};
struct T2 : T1 { int b; };
int f() {
T1 t1 = { 1 };
return reinterpret_cast<T2*>(&t1)->value();
}
I know the cast is not undefined behavior3 by itself; but is the -> operator on its result infringing [basic.lval]/114? or are we saved by [expr.ref]/45?
I want to think it is legal since t1.a and reinterpret_cast<T2*>(&t1)->a have the same address6 (hence the same offset in their representation). But is it?
1) [class.mem]/22 and [class.mem]/23 define what layout-compatible classes are; [class.mem]/25 gives us:
In a standard-layout union with an active member of struct type
T1, it is permitted to read a non-static data member m of another union member of struct typeT2providedmis part of the common initial sequence ofT1andT2; the behavior is as if the corresponding member ofT1were nominated.
2) In [class.mem]/25.
An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of object pointer type is converted to the object pointer type “pointer to
cv T”, the result isstatic_cast<cv T*>(static_cast<cv void*>(v)).
A prvalue of type “pointer to cv1
void” can be converted to a prvalue of type “pointer to cv2T”, whereTis an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. If the original pointer value represents the addressAof a byte in memory andAdoes not satisfy the alignment requirement ofT, then the resulting pointer value is unspecified. Otherwise, if the original pointer value points to an objecta, and there is an objectbof typeT(ignoring cv-qualification) that is pointer-interconvertible witha, the result is a pointer tob. Otherwise, the pointer value is unchanged by the conversion.
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:
- [ rules not applicable ]
- (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),
- [ rules not applicable ]
5) [expr.ref]/4 on postfix expression involving the notation E1.E2 or E1->E2.
If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member if that member is not a bit-field. Its address is also the same as the address of each of its base class subobjects.