为什么 [[no_unique_address]] 对公共数据成员没有影响?

问题描述 投票:0回答:1

考虑以下示例:

template <class T>
struct A {
    [[no_unique_address]] T t;
    int i;
};

struct B {
    long l;
    int i;
};

class C {
    long l;
    int i;
};

GCC 和 Clang 都认为

sizeof(A<B>)
24
,而
sizeof(A<C>)
16
编译器资源管理器

类模板

A<T>
T
作为具有
[[no_unique_address]]
属性的数据成员之一。
B
C
之间的唯一区别是
B
struct
,而
C
class
。我不明白为什么
A<B>
A<C>
大小不一样。换句话说,为什么编译器将类模板
i
的成员
A<T>
嵌入到
C
的尾部填充中,而不嵌入到
B
的尾部填充中?

如果我修改

B
private
的成员访问权限,GCC和Clang都认为
A<B>
的大小是
16
:(Compiler Explorer)

template <class T>
struct A {
    [[no_unique_address]] T t;
    int i;
};

struct B {
private:      // private now
    long l;
    int i;
};

class C {
    long l;
    int i;
};

所以看来差异不是来自

struct
class
,而是来自数据成员的可访问性。

我知道编译器是否将其他成员嵌入到具有

[[no_unique_address]]
属性的成员的尾部填充中是实现定义的,但我猜有一些特殊规则会导致 GCC 和 Clang 中出现相同的奇怪行为。我检查了标准和 ABI 文档,但找不到描述。

c++ layout c++20
1个回答
0
投票

这是因为 ABI 指定布局的方式。

https://itanium-cxx-abi.github.io/cxx-abi/abi.html#pod:

这些类型的

dsizenvsizenvalign 被定义为它们的普通大小和对齐方式。这些属性仅对用作基类的非空类类型重要。我们忽略 POD 的尾部填充,因为解决 CWG 问题 43 之前的标准不允许我们将其用于其他任何用途,而且它有时允许更快地复制类型。

dsize是对象的“数据大小”,这意味着该类型使用了多少字节,不包括尾部填充。

这意味着对于“用于布局目的的 POD”类型,可能的尾部填充被视为该类型数据的一部分,因此不能重用来保存成员

i

当你将任何一个成员设为私有时,它就不再是“为了布局而使用的 POD”,因此

dsize 变成了 sizeof(long) + sizeof(int)

(而不是 
sizeof(B) = 2 * sizeof(long)
),并且可以将下一个成员 
i
 放置在尾部填充。

如果您尝试创建基类子对象,也会出现同样的问题:

template <class T> struct A : T { int i; }; struct B { long l; int i; }; // sizeof = 16, dsize = 16 class C { long l; int i; }; // sizeof = 16, dsize = 12 // A<B>: 16 bytes B, 4 bytes for int i, 4 bytes for padding; sizeof = 24, dsize = 20 // A<C>: 12 bytes C, 4 bytes for int i, no padding; sizeof = 16, dsize = 16 // (The same numbers for `[[no_unique_address]] T t;`)
    
© www.soinside.com 2019 - 2024. All rights reserved.