【发布时间】:2021-05-27 12:51:21
【问题描述】:
我正在使用 C++20 中引入的 [[no_unique_address]] 属性。据我从标准的cppreference article 和dcl.attr.nouniqueaddr 章节中了解到,该属性表明该字段不需要具有与该类的所有其他非静态数据成员不同的地址。因此编译器可以优化结构的内存布局。但是有一件事让我很困惑。
考虑以下示例 (https://godbolt.org/z/fj6nGebcs):
struct Empty {};
struct Test {
[[no_unique_address]] Empty em1;
char f1;
int f2;
[[no_unique_address]] Empty em2;
};
字段em1 的大小为零,并将位于与f1 相同的地址。
对我来说,同样的优化可以应用于em2 似乎是合乎逻辑的,它将具有与f2 相同的地址和零大小。但它不是那样工作的。经过一些实验,我可以说如果在结构的末尾定义了一个带有[[no_unique_address]] 的字段,那么这种优化根本不起作用。所以用最新版本的gcc和clang编译,struct Test的大小将为12(1字节char字段,3字节对齐,4字节int字段,struct Empty字段大小为1字节和 3 字节对齐)。
现在让我们将em2 向上移动(https://godbolt.org/z/Goz9Tj66K):
struct Empty {};
struct Test {
[[no_unique_address]] Empty em1;
char f1;
[[no_unique_address]] Empty em2;
int f2;
};
此次更改后,struct Test 的大小将变为 8,em2 字段将与f2 具有相同的地址。
我不明白为什么会这样。我在clang 中找到了相同结构大小的测试。最新的 gcc 也是如此。
但我不确定,为什么编译器不能像第二个示例一样在上面的第一个示例中应用 [[no_unique_address]] 优化。
有任何技术限制吗?还是和编译器的内部实现有关?
【问题讨论】:
-
这取决于实施的质量,他们可以对两个 sn-ps 做同样的事情。
-
"...
em1字段的大小为零..." - 否 - "...sizeof 的结果是 始终非零,即使应用于空类类型...." en.cppreference.com/w/cpp/language/sizeof -
它们遵循与基类相同的 ABI 规则。而那些旧规则远非完美,在历史上你可以找到诸如“这样更容易实现”、“这样布局算法会慢一些”等论点,现在可能会以不同的方式完成,但不值得打破 ABI 的成本。要回答这个问题,是的,该标准允许编译器做得更好。