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]/11
4? or are we saved by [expr.ref]/4
5?
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 typeT2
providedm
is part of the common initial sequence ofT1
andT2
; the behavior is as if the corresponding member ofT1
were 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
”, whereT
is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. If the original pointer value represents the addressA
of a byte in memory andA
does 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 objectb
of 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.