该标准在示例 2 中告诉我们 1 ,以下内容是合法的:
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
}
现在,添加一个间接级别(成员函数)并删除另一个(成员),我想知道:
以下定义是否明确?
struct T1 { int a; int value() { return a; }};
struct T2 : T1 { int b; };
int f() {
T1 t1 = { 1 };
return reinterpret_cast<T2*>(&t1)->value();
}
我知道演员表本身不是不确定的行为 3 ;但是->
运算符的结果是否侵犯了[basic.lval]/11
4 ?还是由[expr.ref]/4
5 保存?
我想认为这是合法的,因为t1.a
和reinterpret_cast<T2*>(&t1)->a
具有相同的地址 6 (因此其表示中的偏移量相同) 。但是吗?
1) [class.mem]/22
和[class.mem]/23
定义什么是与布局兼容的类; [class.mem]/25
给了我们:
在结构类型为
T1
的活动成员的标准布局联合中,允许读取结构类型为{{的另一个联合成员的非静态数据成员m 1}},前提是T2
是m
和T1
的共同初始序列的一部分;行为就好像T2
的相应成员已被提名。
2)在[class.mem]/25
中。
可以将对象指针显式转换为其他类型的对象指针。将对象指针类型的prvalue v转换为对象指针类型“指向
T1
的指针”时,结果为cv T
。
可以将“指向 cv1
static_cast<cv T*>(static_cast<cv void*>(v))
的指针的prvalue转换为”指向 cv2void
的指针的prvalue,其中T
是对象类型,而 cv2 是与 cv1 相同的cv限定,或具有更大的cv限定。如果原始指针值表示存储器中字节的地址T
,并且A
不满足A
的对齐要求,则未指定结果指针值。否则,如果原始指针值指向对象T
,并且存在类型为a
(忽略cv限定)的对象b
,该指针可以与{{1}进行指针转换},结果是指向T
的指针。否则,转换后指针值将保持不变。
如果程序尝试通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:
- [不适用规则]
- (11.6)聚合或联合类型,其元素或非静态数据成员(包括递归地包括子聚合或所包含的联合的元素或非静态数据成员)中包括上述类型之一,
li>- [不适用规则]
5) [expr.ref]/4
,涉及包含符号a
或b
的后缀表达式。
如果 standard-layout类对象具有任何非静态数据成员,则该对象的地址与其第一个非静态数据成员的地址相同(如果该成员不是位字段) 。其地址也与其每个基类子对象的地址相同。
答案 0 :(得分:2)
不,据我所知是UB。
我看不到[expr.ref] p4如何拯救您。您正在通过其他类型的指针(也不是对象的动态类型)访问T1
类型的对象。
此外,[class.mem] p26不相关,因为您已经违反了[basic.lval] p11。