【问题标题】:C++ lifetime of union member联合成员的 C++ 生命周期
【发布时间】:2020-01-12 15:06:56
【问题描述】:

在当前版本的 C++ 标准草案中,[basic.life]/1 声明:

对象或引用的生命周期是 对象或参考。据说一个变量有空的初始化 如果它是默认初始化的,并且如果它是类类型或 (可能是多维的)数组,该类类型具有 微不足道的默认构造函数。 T 类型对象的生命周期 开始时间:

  • 获得类型 T 的正确对齐和大小的存储,并且

  • 它的初始化(如果有的话)已经完成(包括 vacuous 初始化)([dcl.init]),

除非对象是联合 成员或其子对象,它的生命周期仅在该联合时才开始 member 是联合中的初始化成员([dcl.init.aggr], [class.base.init]),或如 [class.union] 中所述。 [...]

从那段中我了解到,工会成员开始其生命周期的唯一方式是:

  • 该成员“是联合中的已初始化成员”(例如,如果它在 mem-initializer 中被引用),或者
  • [class.union] 中提到的其他方式

然而,[class.union] 中唯一指定联合成员如何开始其生命周期的规范段落是[class.union]/5(但它仅适用于特定类型,即non-classnon-arrayclass 类型以及未删除的 trivial 构造函数,或此类类型的数组)。

下一段[class.union]/6(包括注释和示例,因此不包含规范文本)描述了一种通过使用@987654329 更改联合的活动成员的方法@,比如new (&u.n) N;,在哪里

struct N { N() { /* non-trivial constructor */ } };
struct M { M() { /* non-trivial constructor */ } };

union 
{
    N n;
    M m;
} u;

我的问题是在标准中的哪个位置指定new (&u.n) N; 开始u.n 的生命周期?

谢谢!

【问题讨论】:

  • 你已经有了它:它的初始化(如果有的话)已经完成(包括空初始化)([dcl.init]),。 [dcl.init] 谈new
  • @NathanOliver [basic.life]/1 中强调的部分介绍了开始工会成员生命周期的附加条件。至少我是这么读的。
  • 联合是核心 C++ 中另一个令人深感不安的领域:1)很长一段时间没有明确的联合规则; 2)“左值指的是对象”是一个普遍接受的比喻,即使在专家 C++ 圈子中也是如此;根据定义,分配工会成员是不正确的。 3)“左值引用对象”明确解释了为什么您不能取消引用空 ptr。 C++ 是一团糟!

标签: c++ initialization language-lawyer unions object-lifetime


【解决方案1】:

一个重要的规则是:

[class.union]/1

在联合中,如果一个非静态数据成员的名称引用一个生命周期已经开始但尚未结束的对象 ([basic.life]),则它是活动的。 ...

就这条规则而言,活动成员可以在成员对象开始其生命周期的任何时候更改。规则 [class.union]/5 还允许通过分配给有限类型集的非活动成员来更改活动成员。缺乏单独的放置新规则本身并不允许更改成员。如果它开始了成员的生命周期,那么该成员就是工会的活跃成员。

所以,[basic.life/1] 表示成员的生命周期仅在 [class.union] 如此1 时开始,并且 [class.union/1] 表示该成员仅当其生命周期已开始2 时才处于活动状态。这似乎有点像第 22 条规则。

我以一种有意义的方式阅读规则的最佳尝试是解释placement-new开始成员的生命周期,因此[class.union/1]适用,因此“或如中所述[class.union]" 适用,因此突出显示的异常不适用。接下来我想说因此生命周期开始,但这个逻辑是循环的。

非规范性的 [class.union]/6 非常清楚地表明允许放置 new 是允许的,但规范性规则是复杂的。我会说措辞可以改进。


1(或者当联合使用该成员初始化时,我们正在考虑的不是这种情况)

2(或根据 [class.union]/5 分配后,我们正在考虑的情况并非如此)

【讨论】:

  • 感谢您的回答。我认为 [class.union]/1 没有说明工会成员的生命周期何时开始,它只是描述了工会的活跃成员是如何确定的。我看到“规则流”的方式如下:[basic.life]/1 指出,如果联合成员是“初始化成员”或如 [class.union] 所述,则它的生命周期开始。接下来,[class.union] 中唯一说明联合成员生命周期何时开始的规范文本是第 5 段。[class.union]/1 仅使用使用前一个达到的“结论”规则
  • 这是另一个提醒,“左值指代对象”的说法(谁能说他从未从“专家”那里听说过?)是错误的 ,除非对象在其生命周期开始之前就存在,这意味着其他口号“对象不能重叠(子对象除外)”是错误的。如果您一直在阅读严肃的 C++ 文献,那么您几乎肯定已经阅读过这两种说法。这是个问题。
【解决方案2】:

我的问题是在标准中的哪个位置指定new (&u.n) N; 开始u.n 的生命周期?

无处可去。 Placement new 创建一个新对象,该对象成为每个 [intro.object]/2 的联合成员子对象:

如果在与成员子对象或数组元素e(可能在也可能不在其生命周期内)关联的存储中创建对象,则创建的对象是e 的包含对象,如果:
e 的包含对象的生命周期已经开始但未结束,并且
— 新对象的存储正好覆盖相关的存储位置 与 e,以及
— 新对象与 e 的类型相同(忽略 cv 限定)。

【讨论】:

  • 感谢您的回答。所以placement new-expression创建的对象不是union member,所以[basic.life]/1不适用?
  • @user42768 如果满足 [intro.object]/2 中的条件,则新创建的对象是联合的成员子对象。斜体 union member 是指别的意思(不是成员子对象)吗?
  • [basic.life]/1 声明联合成员(我指的是联合成员的这个含义)仅在特殊条件下才开始其生命周期。因此,在您回答之后,我将 [basic.life]/1 解释如下:联合成员引用的初始对象仅在特殊条件下才开始其生命周期(但是替换它的任何其他对象都不受这些条件的约束)。对吗?
  • 但是在历史上的 C++ 中曾经发生过什么?没有?编译器做对了,因为它没有说? :o
  • @user42768 [class.union] 似乎并没有说现有对象的生命周期开始了。它说创建了一个新对象。我看到你已经打开了一个问题。
【解决方案3】:

此时 C++ 无法定义联合,因为:

  • 根据定义,左值必须引用一个对象
  • 对象有生命周期;目前尚不清楚什么是生命前对象
  • 理论上两个不相关的对象不能在同一个地址,但是生命周期前的对象可以,所以没有生命周期前的对象这种东西

所以可变联合不能在 C++ 中很好地定义,故事结束。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-19
    • 2016-05-20
    • 1970-01-01
    • 2014-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多