Bee*_*ope 12 c++ language-lawyer c++20
考虑以下三个方面struct:
class blub {
int i;
char c;
blub(const blub&) {}
};
class blob {
char s;
blob(const blob&) {}
};
struct bla {
blub b0;
blob b1;
};
Run Code Online (Sandbox Code Playgroud)
在int4 字节的典型平台上,大小、对齐方式和总填充1如下:
struct size alignment padding
-------- ------ ----------- ---------
blub 8 4 3
blob 1 1 0
bla 12 4 6
Run Code Online (Sandbox Code Playgroud)
blub和blob成员的存储之间没有重叠,即使大小 1blob原则上可以“适合” blub.
C++20 引入了no_unique_address属性,它允许相邻的空成员共享相同的地址。它还明确允许使用一个成员的填充来存储另一个成员的上述场景。来自cppreference(强调我的):
指示此数据成员不需要具有与其类的所有其他非静态数据成员不同的地址。这意味着如果成员有一个空类型(例如无状态分配器),编译器可能会优化它以不占用空间,就像它是一个空基一样。如果该成员不为空,则其中的任何尾部填充也可以重新用于存储其他数据成员。
事实上,如果我们在 上使用这个属性blub b0,大小会bla下降到8,所以blob确实存储在blub Godbolt 上。
最后,我们来回答我的问题:
no_unique_address对于不能简单复制的对象,标准(C++11 到 C++20)中的哪些文本可以在没有, 的情况下防止这种重叠?
我需要从上面排除普通可复制 (TC) 对象,因为对于 TC 对象,允许std::memcpy从一个对象到另一个对象,包括成员子对象,如果存储重叠,这将中断(因为所有或部分存储因为相邻成员将被覆盖)2。
1我们将填充简单地计算为结构大小与其所有组成成员的大小之间的差异,递归。
2这就是为什么我定义了复制构造函数: makeblub而blob不是简单的 copyable。
该标准在谈论内存模型时非常安静,并且对其使用的一些术语也不是很明确。但我想我找到了一个可行的论证(可能有点弱)
\n首先,让我们弄清楚什么是对象的一部分。[基本类型]/4 :
\n\n\n类型对象的对象表示是类型对象所占用的对象
\nT序列,其中equals 。类型对象的值表示是参与表示类型值的位的集合Nunsigned charTNsizeof(T)TT。对象表示中不属于值表示的位是填充位。
所以 的对象表示b0包括sizeof(blub) unsigned char,所以 8 个字节。填充位是对象的一部分。
如果没有嵌套在其中,则任何对象都不能占用另一个对象的空间[basic.life]/1.5:
\n\n\n\n
o类型对象的生命周期T在以下情况结束:[...]
\n(1.5) 对象占用的存储被释放,或者被未嵌套的对象重用
\no(1.5) 对象占用的存储被释放,或者被未嵌套在([intro.object])
b0因此,当它占用的存储空间被另一个对象重用时,它的生命周期就会结束,即b1就会结束。我还没有检查过,但我认为标准要求活着的对象的子对象也应该活着(我无法想象这应该如何以不同的方式工作)。
b0 所以占用的存储可能不会被使用b1。我在标准中没有找到“占用”的定义,但我认为合理的解释是“对象表示的一部分”。在描述对象表示的引用中,使用了“take up”一词1。这里,这将是 8 个字节,因此bla至少还需要一个字节b1。
特别是对于子对象(因此除其他非静态数据成员之外)还有规定[intro.object]/9规定(但这是用 C++20 添加的,谢谢@BeeOnRope)
\n\n\n如果一个对象嵌套在另一个对象中,或者如果至少一个对象是零大小的子对象并且它们属于不同类型,则两个具有重叠生命周期(不是位域)的对象可能具有相同的地址;否则,它们具有不同的地址并占用不相交的存储字节。
\n
(强调我的)在这里,我们再次遇到“占用”未定义的问题,我再次主张在对象表示中获取字节。请注意,此[basic.memobj]/footnote 29有一个脚注
\n\n\n根据 \xe2\x80\x9cas-if\xe2\x80\x9d 规则,如果程序无法观察到差异,则允许实现在同一机器地址存储两个对象,或者根本不存储对象([intro.execution] )。
\n
如果编译器可以证明没有可观察到的副作用,则可能允许编译器打破这一点。我认为对于像对象布局这样的基本事物来说这是相当复杂的。也许这就是为什么只有当用户提供没有理由通过添加不相交对象的信息时才进行此优化的原因[no_unique_address]属性来拥有不相交对象的信息时才进行此优化的原因。
tl;dr:填充可能是对象的一部分,并且成员必须是不相交的。
\n1我忍不住添加了一个引用,该引用可能意味着占用:Webster\xe2\x80\x99s Revised Unabridged Dictionary, G. & C. Merriam, 1913(重点是我的)
\n\n\n\n
\n- 保持或填充尺寸;占用……的房间或空间;覆盖或填充;该营地占地五英亩。J·赫歇尔爵士。
\n
如果没有字典爬行,什么样的标准爬行才能完成?
\n