为什么 sizeof(std::variant) 与具有相同成员的结构的大小相同?
Why is sizeof(std::variant) the same size as a struct with the same members?
class 模板 std::variant
表示类型安全的联合。 std::variant
的实例在任何给定时间要么持有其替代类型之一的值,要么不持有任何价值。
sizeof(std::variant<float, int32_t, double>) == 16
但是如果是联合,为什么要那么多space?
struct T1 {
float a;
int32_t b;
double c;
};
struct T2 {
union {
float a;
int32_t b;
double c;
};
};
变量与结构大小相同
sizeof(T1) == 16
sizeof(T2) == 8
我希望联合的大小加上要存储的 4 个字节,哪种类型是活动的。
这里variant
中最大对齐的类型是double
,对齐为8。这意味着整个variant
必须有8的对齐。这也意味着它的大小必须是 8 的倍数,确保在一个数组中每个 variant
将其 double
对齐 8.
但是变体必须存储的不仅仅是最大的类型:它还必须存储标识符或标记以了解当前实例化的类型。因此,即使该标识符仅使用 1 个字节,整个结构也会被填充为 16,因此它的大小是 8 的倍数。
更正确的比较是:
struct
{
union
{
float a;
int32_t b;
double c;
};
int identifier;
};
虽然变体在逻辑上是类型安全的联合体,但不能保证它们的大小等于具有相同成员的原始联合体的大小。
显然,由于变体(与原始联合不同)需要存储有关当前活动类型的信息,因此它必须更大。变体的实际大小取决于体系结构(填充)和实现,因为标准对大小没有限制。
有趣的是,你被巧合骗了。下面的return是16:
sizeof(std::variant<float, int32_t, double, int64_t>)
还有这个:
sizeof(std::variant<float, int32_t, double, int64_t, double>)
所以基本上 std::variant
中有一个内部变量,其大小为 8 个字节(或更少,但对齐到 8 个字节)。那,加上你的工会,就是 16。
基本答案是:出于对齐原因。由于您的一种类型是双精度型,而双精度型是 8 字节且具有 8 字节对齐要求,这意味着变体也具有 8 字节对齐要求。所以它的大小只能是8字节的倍数。正如您自己指出的那样,最小尺寸是最大的类型 + 一些额外的东西来指示哪个成员是活跃的。这意味着它不能容纳 8 个字节,但随后对齐要求强制它一直到 16 个字节。
这就是为什么它不能像您所说的那样是 12 字节的原因。顺便说一句,也没有规则说活动类型存储必须是 4 个字节。事实上,很明显,在绝大多数情况下,你可以通过一个字节来解决问题,它可以区分 255 种类型,加上空状态。我们可以测试一下:
struct null {};
std::cerr << sizeof(std::variant<bool, null>);
我刚刚在 coliru 上测试了这个,它打印出“2”。在这种情况下,1 个字节用于最大类型(bool),1 个字节用于确定哪个类型是活动的。
class 模板 std::variant
表示类型安全的联合。 std::variant
的实例在任何给定时间要么持有其替代类型之一的值,要么不持有任何价值。
sizeof(std::variant<float, int32_t, double>) == 16
但是如果是联合,为什么要那么多space?
struct T1 {
float a;
int32_t b;
double c;
};
struct T2 {
union {
float a;
int32_t b;
double c;
};
};
变量与结构大小相同
sizeof(T1) == 16
sizeof(T2) == 8
我希望联合的大小加上要存储的 4 个字节,哪种类型是活动的。
这里variant
中最大对齐的类型是double
,对齐为8。这意味着整个variant
必须有8的对齐。这也意味着它的大小必须是 8 的倍数,确保在一个数组中每个 variant
将其 double
对齐 8.
但是变体必须存储的不仅仅是最大的类型:它还必须存储标识符或标记以了解当前实例化的类型。因此,即使该标识符仅使用 1 个字节,整个结构也会被填充为 16,因此它的大小是 8 的倍数。
更正确的比较是:
struct
{
union
{
float a;
int32_t b;
double c;
};
int identifier;
};
虽然变体在逻辑上是类型安全的联合体,但不能保证它们的大小等于具有相同成员的原始联合体的大小。
显然,由于变体(与原始联合不同)需要存储有关当前活动类型的信息,因此它必须更大。变体的实际大小取决于体系结构(填充)和实现,因为标准对大小没有限制。
有趣的是,你被巧合骗了。下面的return是16:
sizeof(std::variant<float, int32_t, double, int64_t>)
还有这个:
sizeof(std::variant<float, int32_t, double, int64_t, double>)
所以基本上 std::variant
中有一个内部变量,其大小为 8 个字节(或更少,但对齐到 8 个字节)。那,加上你的工会,就是 16。
基本答案是:出于对齐原因。由于您的一种类型是双精度型,而双精度型是 8 字节且具有 8 字节对齐要求,这意味着变体也具有 8 字节对齐要求。所以它的大小只能是8字节的倍数。正如您自己指出的那样,最小尺寸是最大的类型 + 一些额外的东西来指示哪个成员是活跃的。这意味着它不能容纳 8 个字节,但随后对齐要求强制它一直到 16 个字节。
这就是为什么它不能像您所说的那样是 12 字节的原因。顺便说一句,也没有规则说活动类型存储必须是 4 个字节。事实上,很明显,在绝大多数情况下,你可以通过一个字节来解决问题,它可以区分 255 种类型,加上空状态。我们可以测试一下:
struct null {};
std::cerr << sizeof(std::variant<bool, null>);
我刚刚在 coliru 上测试了这个,它打印出“2”。在这种情况下,1 个字节用于最大类型(bool),1 个字节用于确定哪个类型是活动的。