c++ - 不死物体 ([basic.life]/8) : why is reference rebinding (and const modification) allowed?

标签 c++ language-lawyer constants lifetime placement-new

“亡灵”条款

我将 undead 子句称为 C++ 规则,即在对象销毁后,如果在同一地址创建新对象,则有时可以将其视为与旧对象相同的对象。该规则始终存在于 C++ 中,但对附加条件进行了一些更改。

我被 this question 要求阅读最新的不死条款。 Lifetime [basic.life]/8 中的修改条件为:

(8.1) the storage for the new object exactly overlays the storage location which the original object occupied, and



嗯,呃。不同地址的对象不会是同一个对象。

(8.2) the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and



再说一次,呵呵。

(8.4) neither the original object nor the new object is a potentially-overlapping subobject ([intro.object]).



它不能是基类、经典类(或具有使其地址不唯一的特殊声明的成员)。再说一次,呵呵。

(8.3) the original object is neither a complete object that is const-qualified nor a subobject of such an object, and



现在这很有趣。被替换的对象不能是:
  • 一个完整的 const 对象
  • 完整 const 对象的一部分

  • 另一方面,被复活的对象可以是:
  • 一个 const 成员子对象
  • 此类 const 成员
  • 的子对象
  • const 对象数组中的一个元素

  • 常量子对象

    所以在我看来,所有这些对象 x 都可以复活:

    const 成员子对象
    struct CI {
      const int x;
    };
    
    CI s = { 1 };
    new ((void*)&s.x) int(2);
    int r = s.x; // OK, 2
    

    const 成员的子对象:
    struct T {
      int x;
    };
    
    struct CT {
      const T m = { 1 };
    };
    
    CT s;
    new ((void*)&s.m.x) int (2);
    int r = s.m.x;
    

    const 对象数组中的元素:
    const int x[1] = { 1 };
    new ((void*)&x[0]) int (2);
    int r = x[0];
    

    具有 const 和 reference 成员的类

    带有 const 或引用成员的类类型的对象似乎也不被禁止;复活的对象仍称为 x

    具有 const 成员的类:
    struct CIM {
      CIM(int i): m(i) {}
      const int m;
    };
    
    CIM x(1);
    new ((void*)&x) CIM(2);
    int r = x.m; // OK, 2
    

    具有引用成员的类:
    struct CRM {
      CRM (int &r): m(r) {}
      int &m;
    };
    
    int i=1,j=2;
    CRM x(i);
    new ((void*)&x) CRM(j);
    int r = x.m; // OK, 2
    

    问题
  • 该条款的解释是否正确?
  • 如果是这样,是否还有其他条款禁止这些覆盖操作?
  • 如果是这样,那是故意的吗?为什么改变了?
  • 这是代码生成器的重大变化吗?所有编译器都真的支持吗?他们不是基于 const 成员进行优化,数组的 const 元素是不可变的并且引用不可反弹吗?
  • 奖励问题:这是否会影响具有足够存储类(当然不是动态创建的对象)和足够初始化的 const 对象的 ROM 能力?

  • 注意:我后来添加了奖金,因为在讨论中出现了将常量放入 ROM 的问题。

    最佳答案

    如果与对象生命周期相关的标准的所有要求都不在 [basic-life] 中,那将是令人惊讶的。

    在您引用的标准段落中,“完整”形容词被无意添加到名称“对象”的可能性很小。

    在论文P0137 ,人们可以阅读这个理性(下面@LanguageLawyer 评论中引用的论文):

    This is necessary to allow types such as std::optional to contain const subobjects; the existing restriction exists to allow ROMability, and so only affects complete objects.



    为了让我们放心,我们可以验证编译器确实遵循信中的标准措辞:它们对完整的 const 对象执行常量优化,但对非 const 完整对象的 const 成员子对象不执行优化:

    让我们考虑this code :
    struct A{const int m;};
    
    void f(const int& a);
    
    auto g(){
        const int x=12;
        f(x);
        return x;
    }
    
    auto h(){
        A a{12};
        f(a.m);
        return a.m;
    }
    

    当面向 x86_64 时,Clang 和 GCC 都会生成此程序集:
    g():                                  # @g()
            push    rax
            mov     dword ptr [rsp + 4], 12
            lea     rdi, [rsp + 4]
            call    f(int const&)
            mov     eax, 12     ;//the return cannot be anything else than 12
            pop     rcx
            ret
    h():                                  # @h()
            push    rax
            mov     dword ptr [rsp], 12
            mov     rdi, rsp
            call    f(int const&)
            mov     eax, dword ptr [rsp]  //the content of a.m is returned
            pop     rcx
            ret
    

    返回值放入寄存器eax (根据 ABI 规范:System V x86 处理器特定 ABI):
  • 在函数 g编译器可以自由假设 x无法通过对 f 的调用进行更改因为x是一个完整的 const 对象。所以值 12直接放在eax注册为立即值:mov eax, 12 .
  • 在函数 h编译器不能随意假设 a.m无法通过对 f 的调用进行更改因为a.m不是完整的 const 对象的子对象。所以在调用 f 之后a.m 的值必须从内存加载到 eax : mov eax, dword ptr [rsp] .
  • 关于c++ - 不死物体 ([basic.life]/8) : why is reference rebinding (and const modification) allowed?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59298904/

    相关文章:

    c++ - Boost Console 记录器在设置接收器后将所有消息写入 STDERR

    c++ - 在 c++ 中, `expressions` 只允许在全局范围内用于初始化全局对象。我在哪里可以在标准中找到它?

    c++ - 在 C 和 C++ 中,const 指针对指针的含义是什么?

    c++ - 候选函数不可行 : 1st argument ('const Node *' ) would lose const qualifier

    objective-c - 我在我的 Objective-C 项目中添加了一些 swift 文件,在 swift 文件中定义了一个全局常量。如何在 OC 文件中使用这个全局常量?

    C++ 继承问题

    c++ - C++ 中的别名和引用是一回事吗?

    c++ - 运算符同时重载 = 和 []

    c++ - 通过初始标准转换序列区分用户自定义转换序列

    c# - 可以使用指针修改只读字段吗?但为什么?