考虑以下示例:
template <class T>
struct A {
[[no_unique_address]] T t;
int i;
};
struct B {
long l;
int i;
};
class C {
long l;
int i;
};
Run Code Online (Sandbox Code Playgroud)
GCC 和 Clang 都认为sizeof(A<B>)是24,sizeof(A<C>)而是16。编译器资源管理器
类模板A<T>将属性T作为其数据成员之一[[no_unique_address]]。B和之间的唯一区别C是B是 astruct且C是 a 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;
};
Run Code Online (Sandbox Code Playgroud)
所以看起来差异不是来自struct和class,而是来自数据成员的可访问性。
我知道编译器是否将其他成员嵌入到具有该[[no_unique_address]]属性的成员的尾部填充中是实现定义的,但我猜有一些特殊规则会导致 GCC 和 Clang 中出现相同的奇怪行为。我检查了标准和 ABI 文档,但找不到描述。
这是因为 ABI 指定布局的方式。
https://itanium-cxx-abi.github.io/cxx-abi/abi.html#pod:
这些类型的 dsize、nvsize和nvalign被定义为它们的普通大小和对齐方式。这些属性仅对用作基类的非空类类型重要。我们忽略 POD 的尾部填充,因为CWG 第 43 个问题解决之前的标准不允许我们将其用于其他用途,而且有时它允许更快地复制类型。
dsize是对象的“数据大小”,这意味着该类型使用了多少字节,不包括尾部填充。
这意味着对于“用于布局目的的 POD”类型,可能的尾部填充被视为该类型数据的一部分,因此不能重用来保存成员i(编译器将其视为B不透明的 16 字节,甚至虽然最后 4 个字节是填充的)
当您将任何成员设为私有时,它不再是“出于布局目的的 POD”,因此 dsize变为(sizeof(long) + sizeof(int)而不是sizeof(B) = 2 * sizeof(long)),并且下一个成员i 可以放置在 tail-padding 中。
如果您尝试创建基类子对象,也会出现同样的问题。
#if BASE_CLASS
template <class T>
struct A : T {
int i;
};
#else
struct A {
[[no_unique_address]] T t;
int i;
};
#endif
struct B {
long l;
int i;
}; // dsize = 16, nvalign = 8, sizeof = 16
class C {
long l;
int i;
}; // dsize = 12, nvalign = 8, sizeof = 16 (next multiple of 8)
// A<B>: 16 bytes B, 4 bytes for int i, 4 bytes for padding
// dsize = 20, sizeof = 24
// A<C>: 12 bytes C, 4 bytes for int i, no padding
// dsize = 16, sizeof = 16
// (The same numbers for `[[no_unique_address]] T t;`)
Run Code Online (Sandbox Code Playgroud)
正在引用的 CWG43 讨论了如何memcpy指定在 C++98 中的所有 POD 类型上工作。因此,布局这些类型的类必须在内部包含尾部填充,这样memcpy就不会覆盖任何本来会进入填充的数据。这在 C++03 中已修复,以指定“POD 类型的任何对象(基类子对象除外)T”,但布局规则未更改(大概是为了不破坏您可以使用memcpyPOD 类型的期望,即使他们是基类)。
[[no_unique_address]]被指定为与基类子对象(作为潜在重叠子对象)具有相同的行为,因此此行为是继承的。
| 归档时间: |
|
| 查看次数: |
119 次 |
| 最近记录: |