亡灵对象([basic.life]/8):为什么允许引用重新绑定(和const修改)? [英] Undead objects ([basic.life]/8): why is reference rebinding (and const modification) allowed?

查看:102
本文介绍了亡灵对象([basic.life]/8):为什么允许引用重新绑定(和const修改)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I call the undead clause the C++ rule that after the destruction of an object, if a new object is created at the same address, it can sometimes be considered the same object as the old one. That rule always existed in C++ but with some changes on the additional conditions.

这个问题使我阅读了最新的不死条款. 生命周期中的修订条件为:

[basic.life]/8 :

I was made to read the latest undead clause by this question. The revised conditions in Lifetime [basic.life]/8 are:

(8.1)新对象的存储正好覆盖了存储 原始对象所占据的位置,以及

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

嗯,嗯.位于不同地址的对象将不是同一对象.

Well, duh. An object at a different address would not be the same object.

(8.2)新对象与原始对象的类型相同 (忽略顶级cv限定词)和

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

再次,du.

(8.4)原始对象和新对象都不是 可能重叠的子对象([intro.object]).

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

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

It cannot a base class, classic (or a member with a special declaration that makes its address not unique). Again, duh.

(8.3)原始对象既不是完整对象,也不是 const限定的,也不是此类对象的子对象,并且

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

现在很有趣.被替换的对象不能是:

Now that's interesting. The object being replaced can't be either:

  • 完整的const对象
  • 完整的const对象的一部分

另一方面,要复活的对象可以是:

On the other hand, the object being resurrected can be:

  1. 一个const成员子对象
  2. 此类const成员的子对象
  3. 一个const对象数组中的元素

const子对象

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

const成员子对象

Const member subobject

struct CI {
  const int x;
};

CI s = { 1 };
new ((void*)&s.x) int(2);
int r = s.x; // OK, 2

const成员的子对象:

Subobject of const member:

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对象数组中的元素:

Element in an array of const objects:

const int x[1] = { 1 };
new ((void*)&x[0]) int (2);
int r = x[0];

具有const和引用成员的类

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

具有const成员的类:

Class with a const member:

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

问题

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

注意:我后来添加了奖金,因为讨论中出现了将常量放入ROM的情况.

Note: I added the bonus later because putting constants in ROM came up in the discussion.

推荐答案

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

It would be surprising if all requirement of the standard related to object life-time were not in [basic-life].

在您引用的标准段落中,很少有将完整"形容词无意中添加到名称对象"的情况.

There are few chances that the "complete" adjective has been inadvertedly added to the name "object" in the standard paragraph you cite.

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

In the paper P0137, one can read this rational (paper cited in @LanguageLawyer comment below):

这是必需的,以允许诸如std :: optional之类的类型包含const子对象;存在现有限制以允许ROMability,因此仅影响完整对象.

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成员对象不执行常量优化:

To reassure us, we can verify that compilers do follow the standard wording at the letter: they perform constant optimization for complete const objects but not for const member suboject of non const complete objects:

让我们考虑此代码:

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都会生成此程序集:

Both Clang and GCC generates this assembly when targeting x86_64:

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):

The returned value is placed in register eax (according to the ABI specification: System V x86 processor specific ABI):

  • 在函数g中,编译器可以自由假定不能在调用f的过程中更改x,因为x是完整的const对象.因此,值12作为直接值直接放置在eax寄存器中:mov eax, 12.

  • In the function g the compiler is free to suppose that x can not be changed accross the call to f because x is a complete const object. So the value 12 is placed directly in the eax register as an immediate value: mov eax, 12.

在函数h中,编译器不能随意假设不能在调用f的过程中更改a.m,因为a.m不是完整const对象的子对象.因此,在调用f之后,必须将a.m的值从内存加载到eax:mov eax, dword ptr [rsp].

In the function h the compiler is not free to suppose that a.m can not be changed accross the call to f because a.m is not a suboject of a complete const object. So after the call to f the value of a.m must be loaded from memory to eax : mov eax, dword ptr [rsp].

这篇关于亡灵对象([basic.life]/8):为什么允许引用重新绑定(和const修改)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆