【问题标题】:Why [[no_unique_address]] attribute doesn't work in some cases?为什么 [[no_unique_address]] 属性在某些情况下不起作用?
【发布时间】:2021-05-27 12:51:21
【问题描述】:

我正在使用 C++20 中引入的 [[no_unique_address]] 属性。据我从标准的cppreference articledcl.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 的成本。要回答这个问题,是的,该标准允许编译器做得更好。

标签: c++ c++20


【解决方案1】:

[[no_unique_address]] 的行为始终由编译器自行决定;它永远不需要做任何事情。编译器可以在所有情况下都忽略它,在某些情况下尊重它,在其他情况下忽略它,或者一直尊重它。只要编译器不是随意做的(即:它是一致的),它就可以做它想做的任何事情(在其他布局规则内)。

那么为什么它在一种情况下有效,而在另一种情况下无效?因为这就是编译器供应商实现它的方式。

如果您想充分利用它,请让编译器更容易处理。不要多次使用相同的空类型(这限制了编译器可以做的事情)。首先声明所有空字段。

或者,您必须跟踪编译器正在使用的 ABI 规则。 Linux 上使用的Itanium ABIno_unique_address 有特殊规定。

【讨论】:

    猜你喜欢
    • 2021-11-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-18
    • 1970-01-01
    • 2021-11-20
    • 1970-01-01
    • 2018-11-28
    • 2011-09-04
    相关资源
    最近更新 更多