我有一个朋友将“Base”类型的非基类对象强制转换为“Derived”类类型对象,其中“Derived”是“Base”的派生类,只添加函数,但没有数据。在下面的代码中,我确实x
向派生类添加了一个数据成员
struct A {
int a;
};
struct B : A {
// int x;
int x;
};
A a;
int g(B *b) {
a.a = 10;
b->a++;
return a.a;
}
通过严格的别名分析,GCC(也是 Clang)总是返回10
,而不是11
,因为在定义明确的代码b
中永远不能指向a
。但是,如果我删除B::x
(实际上是我朋友的代码中的情况),GCC 的输出汇编代码不会优化返回访问a.a
并重新加载内存中的值。g
因此,即使我认为它仍然具有未定义的行为,我朋友在 GCC 上调用“有效”的代码(如他所愿)
g((B*)&a);
因此,在本质上相同的两种情况下,GCC 优化了一种情况而不优化另一种情况。是因为b
可以合法指向a
吗?还是因为 GCC 只是不想破坏现实世界的代码?
我测试了陈述的答案
如果您删除 B::x,则 B 满足 9p7 中标准布局类的要求,并且访问变得完美定义,因为这两种类型是布局兼容的,9.2p17。
有两个布局兼容的枚举
enum A : int { X, Y };
enum B : int { Z };
A a;
int g(B *b) {
a = Y;
*b = Z;
return a;
}
的汇编器输出g
返回1
, not 0
, 即使A
和B
布局兼容 (7.2p8)。
所以我的进一步问题是(引用一个答案):“具有完全相同布局的两个类可能被认为是“几乎相同”,并且它们被排除在优化之外。. 有人可以为 GCC 或 Clang 提供证明吗?