我已经通过迷宫找到了解决这个问题的方法,我想我已经对它进行了非常全面的总结.我发布这个作为答案,因为它似乎解释了C语句的(IMO非常误导)意图和C++不继承它的事实.如果我发现进一步的支持材料或情况发生变化,这将随着时间的推移而演变.
这是我第一次尝试总结出一个非常复杂的局面,这似乎不明确的,甚至很多语言的建筑师,所以我会欢迎,关于如何提高这个答案澄清/建议 - 或者只是一个更好的答案,如果任何人有一个.
最后,一些具体的评论通过隐约相关的线程,我发现下面的答案被@tab -并大加赞赏所包含的链接(照明,如果没有定论),GCC和工作组的缺陷报告:答案通过标签上的StackOverflow
GCC链接包含一些有趣的讨论,并揭示了委员会和编译器供应商的一部分相当大的混淆和相互矛盾的解释 - 围绕C和C++ 中union
成员struct
,双关语和别名的主题.
最后,我们链接到主要事件 - 另一个BugZilla线程,错误65892,包含一个非常有用的讨论.特别是,我们找到了两个关键文件中的第一个:
C99中添加行的来源C提案N685是关于union
类型声明可见性的附加条款的起源.通过一些声称(参见GCC线程#2)对"公共初始序列"容差的完全误解,N685确实旨在允许放宽struct
TU内的"公共初始序列" 的别名规则,意识到某些union
包含的实例所说的struct
类型,正如我们从这句话中看到的那样:
建议的解决方案是要求如果通过公共初始序列(如上所述)的别名是可能的,则可以看到联合声明.因此,如果需要,以下TU提供这种别名:
union utag { struct tag1 { int m1; double d2; } st1; struct tag2 { int m1; char c2; } st2; }; int similar_func(struct tag1 *pst2, struct tag2 *pst3) { pst2->m1 = 2; pst3->m1 = 0; /* might be an alias for pst2->m1 */ return pst2->m1; }
根据海湾合作委员会的讨论和下面的评论,如@ ecatmur's,这个提议 - 似乎要求推测性地允许任何struct
类型的别名,在union
这个TU 中有一些可见的实例- 似乎已经受到很大的嘲笑,很少被实施.
显而易见的是,如果没有完全削弱许多优化措施来满足对附加条款的这种解释是多么困难 - 几乎没有什么好处,因为很少有编码人员想要这种保证,而那些做的人只能开启fno-strict-aliasing
(IMO表明更大的问题).如果实施,这种限额更有可能吸引人们并与其他声明的虚假互动union
,而不是有用.
继之以及我在其他地方发表的评论之后,@ Potatoswatter在这里的答案中指出:
可见性部分是故意从C++中省略的,因为它被广泛认为是荒谬和无法实现的.
换句话说,看起来C++故意避免采用这个附加条款,可能是因为它广泛存在的荒谬性.在要求"记录"引用时,Potatoswatter提供了关于线程参与者的以下关键信息:
那次讨论中的人基本上都是"记录在案".Andrew Pinski是一个铁杆GCC后端人.Martin Sebor是一名活跃的C委员会成员.Jonathan Wakely是一名活跃的C++委员会成员和语言/图书馆实施者.该页面比我能写的任何内容都更具权威性,清晰性和完整性.
Potatoswatter,在相同的SO纱线之上相连,得出结论,C++故意排除这条线,离开指针无需特殊处理(或者在最好的,实现定义的处理)到公共初始序列.他们的待遇是否将在未来具体确定,与其他任何指针相比,还有待观察; 与我下面关于C的最后一节相比.目前,它不是(而且,IMO,这是好的).
这对C++和实际的C实现意味着什么?因此,从N685的邪恶行......" 铸一边" ......我们又回到了假设指针进入公共初始序列没有特殊的混叠方面.仍然.值得确认的是,没有它,C++中的这一段意味着什么.好吧,上面的第二个GCC线程链接到另一个gem:
C++缺陷1719.该提案已达到 DRWP状态:"DR问题的解决方案反映在当前的工作文件中.工作文件是该标准未来版本的草案" -引用.这是在C++之后的14或者至少在我在这里的最终草案之后(N3797) - 并提出了一个重要的,并且在我看来有启发性地重写了这一段的措辞,如下所示.我正在强调我认为是重要的变化, {这些评论}是我的:
在具有活动成员 的标准布局联合中{"active"表示
union
结构类型的实例,而不仅仅是类型}(9.5 [class.union]),T1
允许读取 {以前"检查"}非静态数据构件m
另一联合成员的结构类型的T2
提供m
是共同的初始序列的一部分T1
和T2
.[ 注意:通过非易失性glvalue读取volatile对象具有未定义的行为(7.1.6.1 [dcl.type.cv]). - 尾注]
这似乎澄清旧措辞的含义是:对我来说,它说,任何明确允许中"双关语" union
成员struct
s的公共初始序列,必须做到通过一个实例母公司union
-而不是基于的类型structs
(例如指向它们的指针传递给某个函数).这个措辞似乎排除了任何其他解释,即 N685.我会说,C会采取这种做法.嘿,说到哪,见下文!
结果是 - 正如@ecatmur和GCC门票所证明的那样 - 这在C++中定义了这样的union
成员struct
,实际上在C中,受到与任何其他2个官方无关指针相同的严格别名规则的约束.现在可以更清楚地定义能够读取非活动union
成员struct
的公共初始序列的明确保证,不包括N685针对C 尝试的模糊且难以想象的繁琐强制执行"可见性" .通过此定义,主要编译器具有一直表现为C++的预期.至于C?
同样非常值得注意的是,C委员会成员Martin Sebor也希望用这种优秀的语言来解决这个问题:
Martin Sebor 2015-04-27 14:57:16 UTC如果你们其中一个人可以解释它的问题我愿意写一篇论文并将其提交给WG14并要求更改标准.
Martin Sebor 2015-05-13 16:02:41 UTC我上周有机会与Clark Nelson讨论这个问题.Clark过去曾致力于改进C规范的混叠部分,例如在N1520(http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1520.htm).他同意,就像N1520中指出的问题一样,这也是一个值得WG14重新审视和修复的突出问题."
Potatoswatter鼓舞人心地总结道:
C和C++委员会(通过马丁和克拉克)将试图找到共识并敲定措辞,以便标准最终能说出它意味着什么.
我们只能希望!
再次,欢迎所有进一步的想法.
我已经通过迷宫找到了解决这个问题的方法,我想我已经对它进行了非常全面的总结.我发布这个作为答案,因为它似乎解释了C语句的(IMO非常误导)意图和C++不继承它的事实.如果我发现进一步的支持材料或情况发生变化,这将随着时间的推移而演变.
这是我第一次尝试总结出一个非常复杂的局面,这似乎不明确的,甚至很多语言的建筑师,所以我会欢迎,关于如何提高这个答案澄清/建议 - 或者只是一个更好的答案,如果任何人有一个.
最后,一些具体的评论通过隐约相关的线程,我发现下面的答案被@tab -并大加赞赏所包含的链接(照明,如果没有定论),GCC和工作组的缺陷报告:答案通过标签上的StackOverflow
GCC链接包含一些有趣的讨论,并揭示了委员会和编译器供应商的一部分相当大的混淆和相互矛盾的解释 - 围绕C和C++ 中union
成员struct
,双关语和别名的主题.
最后,我们链接到主要事件 - 另一个BugZilla线程,错误65892,包含一个非常有用的讨论.特别是,我们找到了两个关键文件中的第一个:
C99中添加行的来源C提案N685是关于union
类型声明可见性的附加条款的起源.通过一些声称(参见GCC线程#2)对"公共初始序列"容差的完全误解,N685确实旨在允许放宽struct
TU内的"公共初始序列" 的别名规则,意识到某些union
包含的实例所说的struct
类型,正如我们从这句话中看到的那样:
建议的解决方案是要求如果通过公共初始序列(如上所述)的别名是可能的,则可以看到联合声明.因此,如果需要,以下TU提供这种别名:
union utag { struct tag1 { int m1; double d2; } st1; struct tag2 { int m1; char c2; } st2; }; int similar_func(struct tag1 *pst2, struct tag2 *pst3) { pst2->m1 = 2; pst3->m1 = 0; /* might be an alias for pst2->m1 */ return pst2->m1; }
根据海湾合作委员会的讨论和下面的评论,如@ ecatmur's,这个提议 - 似乎要求推测性地允许任何struct
类型的别名,在union
这个TU 中有一些可见的实例- 似乎已经受到很大的嘲笑,很少被实施.
显而易见的是,如果没有完全削弱许多优化措施来满足对附加条款的这种解释是多么困难 - 几乎没有什么好处,因为很少有编码人员想要这种保证,而那些做的人只能开启fno-strict-aliasing
(IMO表明更大的问题).如果实施,这种限额更有可能吸引人们并与其他声明的虚假互动union
,而不是有用.
继之以及我在其他地方发表的评论之后,@ Potatoswatter在这里的答案中指出:
可见性部分是故意从C++中省略的,因为它被广泛认为是荒谬和无法实现的.
换句话说,看起来C++故意避免采用这个附加条款,可能是因为它广泛存在的荒谬性.在要求"记录"引用时,Potatoswatter提供了关于线程参与者的以下关键信息:
那次讨论中的人基本上都是"记录在案".Andrew Pinski是一个铁杆GCC后端人.Martin Sebor是一名活跃的C委员会成员.Jonathan Wakely是一名活跃的C++委员会成员和语言/图书馆实施者.该页面比我能写的任何内容都更具权威性,清晰性和完整性.
Potatoswatter,在相同的SO纱线之上相连,得出结论,C++故意排除这条线,离开指针无需特殊处理(或者在最好的,实现定义的处理)到公共初始序列.他们的待遇是否将在未来具体确定,与其他任何指针相比,还有待观察; 与我下面关于C的最后一节相比.目前,它不是(而且,IMO,这是好的).
这对C++和实际的C实现意味着什么?因此,从N685的邪恶行......" 铸一边" ......我们又回到了假设指针进入公共初始序列没有特殊的混叠方面.仍然.值得确认的是,没有它,C++中的这一段意味着什么.好吧,上面的第二个GCC线程链接到另一个gem:
C++缺陷1719.该提案已达到 DRWP状态:"DR问题的解决方案反映在当前的工作文件中.工作文件是该标准未来版本的草案" -引用.这是在C++之后的14或者至少在我在这里的最终草案之后(N3797) - 并提出了一个重要的,并且在我看来有启发性地重写了这一段的措辞,如下所示.我正在强调我认为是重要的变化, {这些评论}是我的:
在具有活动成员 的标准布局联合中{"active"表示
union
结构类型的实例,而不仅仅是类型}(9.5 [class.union]),T1
允许读取 {以前"检查"}非静态数据构件m
另一联合成员的结构类型的T2
提供m
是共同的初始序列的一部分T1
和T2
.[ 注意:通过非易失性glvalue读取volatile对象具有未定义的行为(7.1.6.1 [dcl.type.cv]). - 尾注]
这似乎澄清旧措辞的含义是:对我来说,它说,任何明确允许中"双关语" union
成员struct
s的公共初始序列,必须做到通过一个实例母公司union
-而不是基于的类型structs
(例如指向它们的指针传递给某个函数).这个措辞似乎排除了任何其他解释,即 N685.我会说,C会采取这种做法.嘿,说到哪,见下文!
结果是 - 正如@ecatmur和GCC门票所证明的那样 - 这在C++中定义了这样的union
成员struct
,实际上在C中,受到与任何其他2个官方无关指针相同的严格别名规则的约束.现在可以更清楚地定义能够读取非活动union
成员struct
的公共初始序列的明确保证,不包括N685针对C 尝试的模糊且难以想象的繁琐强制执行"可见性" .通过此定义,主要编译器具有一直表现为C++的预期.至于C?
同样非常值得注意的是,C委员会成员Martin Sebor也希望用这种优秀的语言来解决这个问题:
Martin Sebor 2015-04-27 14:57:16 UTC如果你们其中一个人可以解释它的问题我愿意写一篇论文并将其提交给WG14并要求更改标准.
Martin Sebor 2015-05-13 16:02:41 UTC我上周有机会与Clark Nelson讨论这个问题.Clark过去曾致力于改进C规范的混叠部分,例如在N1520(http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1520.htm).他同意,就像N1520中指出的问题一样,这也是一个值得WG14重新审视和修复的突出问题."
Potatoswatter鼓舞人心地总结道:
C和C++委员会(通过马丁和克拉克)将试图找到共识并敲定措辞,以便标准最终能说出它意味着什么.
我们只能希望!
再次,欢迎所有进一步的想法.
我怀疑这意味着不仅可以通过联合类型,而且可以在联合之外访问这些公共部分.也就是说,假设我们有这个:
union u { struct s1 m1; struct s2 m2; };
现在假设在某个函数中我们有一个struct s1 *p1
指针,我们知道这个指针是从m1
这种联合的成员中解除的.我们可以将它转换为struct s2 *
指针并仍然访问与之相同的成员struct s1
.但是在范围的某处,union u
必须显示声明.它必须是完整的声明,它通知编译器成员是struct s1
和struct s2
.
可能的意图是,如果范围中存在这样的类型,则编译器知道struct s1
并且struct s2
是别名的,因此通过struct s1 *
指针的访问被怀疑真正访问a struct s2
或反之亦然.
如果没有任何可见的联合类型以这种方式连接这些类型,就没有这样的知识; 可以应用严格别名.
由于C++中没有措辞,那么为了利用该语言中的"常见初始成员放松"规则,您必须通过联合类型路由访问,这通常是通常所做的:
union u *ptr_any; // ... ptr_any->m1.common_initial_member = 42; fun(ptr_any->m2.common_initial_member); // pass 42 to fun