union通过@ecatmur(/sf/answers/2209049671/)通过引用以下位来讨论关于类型惩罚的大部分未实现或实现定义的性质,关于标准的豁免 -布局structs具有成员类型的"公共初始序列":
C11(6.5.2.3结构和联合成员 ; 语义):
[...]如果一个联合包含几个共享一个共同初始序列的结构(见下文),并且如果联合对象当前包含这些结构中的一个,则允许检查其中任何一个的公共初始部分.完整的工会类型的声明是可见的.如果对应的成员具有一个或多个初始成员的序列的兼容类型(并且对于位字段,具有相同的宽度),则两个结构共享 共同的初始序列.
C++ 03([class.mem]/16):
如果POD-union包含两个或多个共享公共初始序列的POD结构,并且如果POD-union对象当前包含这些POD结构中的一个,则允许检查它们中的任何一个的公共初始部分.如果对应的成员具有一个或多个初始成员的序列的布局兼容类型(并且对于位字段,具有相同的宽度),则两个POD结构共享共同的初始序列.
这两个标准的其他版本有相似的语言; 从C++ 11开始,使用的术语是标准布局而不是POD.
由于不需要重新解释,这不是真正的类型惩罚,只是应用于union成员访问的名称替换.C++ 17(臭名昭着的P0137R1)的提议使得这种语言明确地使用了"访问就像其他结构成员被提名一样"的语言.
但请注意粗体 - " 在任何地方都可以看到完整类型的联合声明 " - C11中存在的条款,但在2003年,2011年或2014年的C++草案中没有任何内容(几乎完全相同,但后来的版本取代了" POD"使用新术语标准布局).在任何情况下,在union任何C++标准的相应部分中都完全没有' 类型位的可见声明.
@loop和@ Mints97,这里 - /sf/answers/1997029261/ - 显示这一行在C89中也没有出现,首先出现在C99中,然后保留在C中(尽管如此,从来没有过滤过到C++).
[剪掉 - 看我的回答]
从那以后,我的问题是:
这是什么意思?什么被归类为"可见声明"?该条款是否旨在缩小 - 或扩大 - 这种"惩罚"定义行为的背景范围?
我们是否假设C++中的这种遗漏是非常慎重的?
C++与C不同的原因是什么?C++是否只是从C89"继承"了这个,然后决定 - 或者更糟,忘记 - 与C99一起更新?
如果差异是故意的,那么 …
我一直在研究在C11前C中实现结构"多态"的非传统方式.假设我们有2个结构:
struct s1 {
int var1;
char var2;
long var3;
};
struct s2 {
int var1;
char var2;
long var3;
char var4;
int var5;
};
Run Code Online (Sandbox Code Playgroud)
在大多数编译器中,我们可以安全地在指向两者的指针之间进行转换,然后在没有填充的情况下访问公共的第一个成员.但是,这不是标准化的行为.
现在,我在C标准中找到了以下C89行:
为了简化联合的使用,我们做了一个特殊的保证:如果一个联合包含几个共享一个共同初始序列的结构,并且如果联合对象当前包含这些结构中的一个,则允许检查任何共同的初始部分.他们 如果相应的成员具有一个或多个初始成员的序列的兼容类型,则两个结构共享共同的初始序列.
它还说明了以下内容:
指向联合对象的指针(适当地强制转换)指向其每个成员(或者如果成员是位字段,则指向它所在的单元),反之亦然.
现在,如果我创建这两个结构的联合:
union s2_polymorphic {
struct s1 base;
struct s2 derived;
};
Run Code Online (Sandbox Code Playgroud)
并以这种方式使用它:
union s2_polymorphic test_s2_polymorphic, *ptest_s2_polymorphic;
struct s2 *ptest_s2;
struct s1 *ptest_s1;
ptest_s2_polymorphic = &test_s2_polymorphic;
ptest_s2 = (struct s2*)ptest_s2_polymorphic;
ptest_s2->var1 = 1;
ptest_s2->var2 = '2';
ptest_s1 = (struct s1*)ptest_s2;
printf("ptest_s1->var1 = %d\n", ptest_s1->var1);
printf("ptest_s1->var2 = %c\n", ptest_s1->var2);
Run Code Online (Sandbox Code Playgroud)
编译并运行良好,并在gcc(GCC)4.8.3 …
我知道标准中明确允许以下内容:
int n = 0;
char *ptr = (char *) &n;
cout << *ptr;
Run Code Online (Sandbox Code Playgroud)
那这个呢?
alignas(int) char storage[sizeof(int)];
int *ptr = (int *) &storage[0];
*ptr = 0;
cout << *ptr;
Run Code Online (Sandbox Code Playgroud)
本质上,我问的是,别名规则是否允许通过指向另一种类型的指针访问一系列字符.我想参考标准的部分,如果可能的话,表明这种或那种方式.
标准的某些部分让我感到矛盾; (3.10.10)似乎表明在假设动态类型storage不是的情况下它将是未定义的行为int.然而,动态类型的定义对我来说并不清楚,而且存在std::aligned_storage会让我相信这是可能的.
struct A
{
int a = 1;
short b = 2;
char c = 3;
}
struct B
{
using arr_type = array<A,3>;
char asd = 0;
A a1;
A a2;
A a3;
// is this safe to use to loop trough all 3 elements?
arr_type* p1 = reinterpret_cast<arr_type*>(&a1);
// or maybe this one?
A* p2 = &a1;
};
Run Code Online (Sandbox Code Playgroud)
我可以安全地使用p1或p2循环播放a1...a3吗?
B b;
for (int i = 0; i < 3; i++)
{
cout << …Run Code Online (Sandbox Code Playgroud)