为什么 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 个字节用于确定哪个类型是活动的。