我已经在迷宫中找到了一些关于这方面的重要资料,并且我想我已经对此有了相当全面的总结。我将其发布为答案,因为它似乎解释了 C 子句的(在我看来非常误导)意图以及 C++ 不继承它的事实。如果我发现更多支持材料或情况发生变化,这将随着时间的推移而发展。
这是我第一次尝试总结一个非常复杂的情况,即使对于许多语言架构师来说,这似乎也定义不明确,所以我欢迎关于如何改进这个答案的澄清/建议 - 或者如果有人有更好的答案,只是一个更好的答案。
最后,一些具体的评论
通过模糊相关的线程,我找到了 @tab 的以下答案 - 并且非常赞赏其中包含的(具有启发性,如果不是决定性的)GCC 和工作组缺陷报告的链接:StackOverflow 上的按选项卡回答 https://stackoverflow.com/a/19807355
GCC 链接包含一些有趣的讨论,并揭示了委员会和编译器供应商的大量混乱和相互冲突的解释 - 围绕以下主题union
member struct
C 和 C++ 中的 s、双关语和别名。
最后,我们链接到主要事件 - 另一个 BugZilla 线程,错误 65892 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65892,包含一个极其有用的讨论。特别是,我们找到了两个关键文档中的第一个:
C99 中添加的行的由来
C提案N685 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n685.htm是关于可见性的附加条款的起源union
类型声明。有些人声称(参见 GCC 线程 #2)完全误解了“共同初始序列”余量,N685 确实是旨在允许放宽“公共初始序列”的别名规则struct
TU 内的 s 意识到某些union
包含上述实例struct
types,正如我们从这句话中可以看到的:
建议的解决方案是要求联合声明可见
是否可以通过公共初始序列(如上所示)使用别名。
因此,如果需要,以下 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;
}
从 GCC 的讨论和下面的评论(例如 @ecatmur 的评论)来看,该提案 - 似乎强制要求推测允许任何别名使用struct
在某些内部有某个实例的类型union
对此 TU 可见 -似乎受到了很大的嘲笑并且很少被实施.
很明显,在不完全削弱许多优化的情况下满足对附加条款的这种解释是多么困难 - 几乎没有什么好处,因为很少有编码员会想要这种保证,而那些想要的人可以打开fno-strict-aliasing
(国际海事组织指出了更大的问题)。如果实施,这项津贴更有可能让人们出局并与其他声明进行虚假互动。union
s,而不是有用。
C++ 中省略该行
继此以及我在其他地方发表的评论之后,@Potatoswatter 在这个答案中 https://stackoverflow.com/a/19805106指出:
C++ 中故意省略了可见性部分,因为它被广泛认为是可笑且无法实现的。
换句话说,看起来 C++ 故意避免采用这个添加的子句,可能是因为它被广泛认为是荒谬的。在要求对此进行“记录”引用时,Potatoswatter 提供了有关该帖子参与者的以下关键信息:
参与讨论的人基本上都是“记录在案”的。 Andrew Pinski 是一位铁杆 GCC 后端人员。 Martin Sebor 是一名活跃的 C 委员会成员。 Jonathan Wakely 是一名活跃的 C++ 委员会成员和语言/库实现者。该页面比我能写的任何内容都更加权威、清晰和完整。
Potatoswatter 在上面链接的同一个 SO 线程中得出结论,C++ 故意排除了这一行,对指向公共初始序列的指针没有留下特殊处理(或者最多是实现定义的处理)。与任何其他指示相比,它们的治疗将来是否会被具体定义,还有待观察;与我下面关于 C 的最后一节相比。但目前,它还不是(IMO,这很好)。
这对于 C++ 和实际的 C 实现意味着什么?
所以,N685 的邪恶线路……”cast除了’...我们回到假设指向公共初始序列的指针在别名方面并不特殊。仍然。值得确认的是,如果没有它,这一段在 C++ 中的含义是什么。好吧,上面的第二个 GCC 线程链接到另一个 gem:
C++ 缺陷 1719 http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1719。该提案已达DRWP状态:“DR 问题,其解决方案反映在当前工作文件中。该工作文件是标准未来版本的草案”-cite https://social.msdn.microsoft.com/Forums/sqlserver/en-US/700569a7-18d3-4ddd-af0b-62bfe5aaf4d1/what-is-the-fastest-way-to-find-a-defect-in-the-c-standard-core-language-defect-report?forum=vcgeneral。这要么是 C++14 之后的事情,要么至少是在我这里的最终草案之后 (N3797) - 并提出了重要的是,在我看来,重写本段的措辞具有启发性, 如下。我将我认为重要的变化加粗,并且{这些评论}是我的:
在标准布局中与活跃成员的联盟 {“活动”表示union
实例,而不仅仅是输入}(9.5 [类.联盟])
结构体类型的T1
,允许read {以前的“检查”}非静态数据成员m
另一位工会成员的结构体类型的T2
假如m
是的一部分
共同的初始序列T1
and T2
. [Note:读取易失性对象
通过非易失性左值具有未定义的行为(7.1.6.1
[dcl.type.cv])。 ——尾注]
这似乎澄清了旧措辞的含义:对我来说,它说任何特别允许的其中‘双关语’union
member struct
必须完成具有共同初始序列的操作通过实例父母的union
- 而不是基于类型structs
(例如,指向它们的指针传递给某个函数)。这个措辞似乎排除了任何其他解释,a laN685。我想说,C 最好采用这个。嘿嘿,说到这里,请看下文!
结果是 - 正如 @ecatmur 和 GCC 票证中很好地证明的那样 - 这使得such union
member struct
根据 C++ 中的定义,以及实际上在 C 中的定义,与任何其他 2 个正式不相关的指针一样,遵循相同严格的别名规则。能够读取非活动的公共初始序列的显式保证union
member struct
现在的定义更加明确,不包括模糊且难以想象的繁琐的强制“可见性”尝试过由 N685 为 C 编写。根据这个定义,主要编译器的行为符合 C++ 的预期。至于C?
C 中此行的可能反转/C++ 中的澄清
同样值得注意的是,C 委员会成员 Martin Sebor 也希望用这种精美的语言来解决这个问题:
马丁·塞博尔 2015-04-27 14:57:16 UTC如果你们中有人能解释其中的问题,我愿意写一篇论文并将其提交给 WG14 并请求更改标准。
马丁·塞博尔 2015-05-13 16:02:41 UTC上周我有机会与克拉克·尼尔森讨论这个问题。 Clark 过去一直致力于改进 C 规范的别名部分,例如在 N1520 中(http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1520.htm http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1520.htm)。他同意,就像 N1520 中指出的问题一样,这也是一个值得 WG14 重新审视和解决的突出问题。”
Potatoswatter 鼓舞人心地得出结论:
C 和 C++ 委员会(通过 Martin 和 Clark)将尝试达成共识并敲定措辞,以便标准最终能够说出其含义。
我们只能希望!
再次强调,欢迎所有进一步的想法。