代码之家  ›  专栏  ›  技术社区  ›  Brian Bi

为了对象替换的目的,对象的“名称”究竟是什么?

  •  4
  • Brian Bi  · 技术社区  · 7 年前

    根据[basic.life]/8,

    如果在对象的生存期结束之后,并且在重新使用该对象所占用的存储之前,或者 释放后,将在原始对象占用的存储位置创建一个新对象,该指针 指向原始对象、引用原始对象的引用或原始对象的名称 对象将自动引用新对象,并且在新对象的生存期开始后,可以 用于操作新对象,如果:

    • 新对象的存储正好覆盖原始对象占用的存储位置,并且
    • 新对象的类型与原始对象的类型相同(忽略顶级cv限定符),并且
    • 原始对象的类型不是const限定的,并且如果是类类型,则不包含任何非静态 类型为const限定类型或引用类型的数据成员,以及
    • 原始对象是类型为 T 新的对象是 类型的对象 T (也就是说,它们不是基类子对象)。

    …[ 注: 如果不满足这些条件,可以从 指针,通过调用 std::launder (21.6)。–尾注]

    该标准包含一个示例,该示例演示 const 子对象,“原始对象的名称”无法引用新对象,使用该名称将导致ub。它位于[intro.object]/2:

    对象可以包含其他对象,称为 子对象 . 子对象可以是 成员子对象 (12.2),A 基础 类子对象 (第13条),或数组元素。不是任何其他对象的子对象的对象是 称为 完整对象 . 如果对象是在与成员子对象或数组关联的存储中创建的 要素 e (可能在其生存期内,也可能不在其生存期内),创建的对象是 e 包含 对象如果:

    • 生命周期 e 包含对象的已开始但未结束,并且
    • 新对象的存储正好覆盖与 e
    • 新对象的类型与 e (忽略简历资格)。

    [ 注: 如果子对象包含引用成员或 康斯特 子对象,原始子对象的名称 无法用于访问新对象(6.8)。艾斯 尾音 ] 例子:

    struct X { const int n; };
    union U { X x; float f; };
    void tong() {
      U u = {{ 1 }};
      u.f = 5.f;                          // OK, creates new subobject of u (12.3)
      X *p = new (&u.x) X {2};            // OK, creates new subobject of u
      assert(p->n == 2);                  // OK
      assert(*std::launder(&u.x.n) == 2); // OK
      assert(u.x.n == 2);                 // undefined behavior, u.x does not name new subobject
    }
    

    然而,在我看来,[basic.life]/8并没有将左值转换为右值 u.x.n 定义的行为是不相关的,因为它是由[expr.ref]/4.2给出的定义的行为,它有以下关于类成员访问表达式的内容 E1.E2 :

    如果 E2 是非静态数据成员,类型为 E1 CQ1 VQ1 X ,以及 E2 CQ2 VQ2 T 艾尔, 表达式指定第一个表达式指定的对象的命名成员。…

    我对这个的理解是 u.x 生成一个左值,它引用 现在的 x 任何对象的子对象 u 当前引用。因为,根据[intro.object]/2,新的 X 对象代替 联合国 引起新的 X 对象实际上是 U ,执行左值到右值的转换 U.X.N 应该定义清楚。

    如果我们假设这个例子中的ub反映了标准的意图,那么我们必须阅读[basic.life]/8 尽管如此 某些表达式似乎可以访问新对象(在本例中,由于[expr.ref]/4.2),但如果它们试图使用原始对象的“名称”进行访问,则它们仍然是ub。(或者,实际上,编译器可以假设“name”继续引用原始对象,因此不会重新读取 康斯特 成员的值。)

    不过,一般情况下,我不认为 联合国 算作“命名” X 子对象 U ,因为我认为子对象没有名称。因此,[basic.life]/8似乎是说ub发生在某些特定的情况下,但没有准确解释这些情况是什么。

    因此,我的问题是:

    1. 我说的[basic.life]对吗/8 原因 这个例子要包含ub而不是仅仅给出它定义的行为?
    2. 有精确的规格说明吗 哪一个 案例是由[basic.life]/8给出的?
    3. 当[basic.life]/8导致ub( 即。, 什么时候 STD:洗熨 需要吗?
    1 回复  |  直到 7 年前
        1
  •  3
  •   Nicol Bolas    7 年前

    我对这个的理解是 u.x 产生一个左值,表示当前 x 任何对象的子对象 u 当前引用。

    这是真的。你误解的是 X “子对象”是指。

    当你这样做的时候 new (&u.x) X {2} ,它在 联合国 . 然而,在标准中没有提到这个对象是命名的 X 联合国 . 是的,它是 U 但是 没有名字 . 标准中没有任何地方说新创建的子对象有这个名称,或者任何与此相关的名称。

    确实,如果你说的是真的,[基本生活]/8并且 launder 根本不需要存在,因为另一个对象的覆盖存储中的对象总是可以通过旧对象的名称访问的。

    不过,一般情况下,我不认为 联合国 算作“命名” X 子对象 U ,因为我认为子对象没有名称。

    我不知道您是怎么得出这个结论的,因为您引用了规范的一部分,其中明确指出成员子对象可以有名称:

    表达式指定 命名成员 对象的

    增加了重点。这清楚地告诉我成员子对象有一个名称和表达式 联合国 指定该特定成员子对象的名称(而不仅仅是该地址中具有适当类型的任何成员子对象)。

    也就是说,就像 U 具体指的是 U , 联合国 具体指 X -声明声明的对象的命名成员 U .