【问题标题】:Why is sizeof(std::variant) the same size as a struct with the same members?为什么 sizeof(std::variant) 与具有相同成员的结构的大小相同?
【发布时间】:2017-08-08 18:47:35
【问题描述】:

类模板std::variant 代表一个类型安全的联合。在任何给定时间,std::variant 的实例要么持有其替代类型之一的值,要么不持有任何值。

sizeof(std::variant<float, int32_t, double>) == 16

但如果是联合,为什么要占用这么多空间呢?

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 个字节,哪种类型是活动的。

【问题讨论】:

  • 您使用的是 32 位系统还是 64 位系统?您使用的是哪个编译器/标准库?
  • 32 位,以及带有 /std:c++latest 选项的 Visual Studio
  • 即使是这样,如果这是标准布局,它也需要 16 个字节。因为你的结构中最大的单词是 8 个字节,在你的情况下,4 个字节将被填充到 8。删除双精度,它的大小将是 8。
  • 巧合。添加更多成员。

标签: c++ unions c++17


【解决方案1】:

这里variant中对齐最大的类型是double,对齐为8。这意味着整个variant必须对齐为8。这也意味着它的大小必须是的倍数8,确保在一个数组中,每个variantdouble 都与8 对齐。

但是变体必须存储的不仅仅是最大的类型:它还必须存储标识符或标签以了解当前实例化的类型。因此,即使该标识符仅使用 1 个字节,整个结构也会被填充为 16,使其大小为 8 的倍数。

更正确的比较是:

struct
{
    union
    {
        float a;
        int32_t b;
        double c;
    };
    int identifier;
};

【讨论】:

  • 值得注意的是,至少在某些系统上,开销只有一个字节(至少在选项足够少的情况下)。在this machinesizeof(std::variant&lt;char, char[1000], char[2000]&gt;) == 2001
  • @DanielH 所有类型中最大的对齐方式是1,它允许identifier 只占用一个字节而没有任何填充。将其中之一设为int,您将看到填充。您在这里展示的是标准库实现细节,而不是真正的机器细节。
  • 是的;我的意思不是没有填充或类似帽子的东西,而是像这样的大型类型很明显存储被重用,或者你需要3002字节来存储所有内容。我想你是对的,我引用了错误的实现部分;我的意思是说这是该实现的一个特性。
【解决方案2】:

基本答案是:出于对齐的原因。由于您的类型之一是双精度型,而双精度型是 8 字节且具有 8 字节对齐要求,这意味着该变体也具有 8 字节对齐要求。所以它的大小只能是 8 个字节的倍数。正如您自己所指出的,最小尺寸是最大的类型+附加的东西以指示哪个成员处于活动状态。这意味着它不能容纳 8 个字节,但是对齐要求会强制它一直到 16 个字节。

这就是为什么它不能是 12 字节的原因,正如你所推理的那样。顺便说一句,也没有规定主动类型存储必须是 4 个字节。事实上,很明显,在绝大多数情况下,你可以用一个字节来区分 255 种类型,再加上空状态。我们可以测试一下:

struct null {};
std::cerr << sizeof(std::variant<bool, null>);

我刚刚在 coliru 上测试了这个,它打印出“2”。在这种情况下,1 个字节用于最大类型(bool),1 个字节用于确定哪种类型处于活动状态。

【讨论】:

  • sizeof(null) 也是 1 个字节,而不是 0。
【解决方案3】:

虽然变体在逻辑上是类型安全的联合,但不能保证它们的大小等于具有相同成员的原始联合的大小。

很明显,由于变体(与原始联合不同)需要存储有关当前活动类型的信息,因此它必须更大。变体的实际大小取决于架构(填充)和实现,因为标准对大小没有限制。

【讨论】:

  • 很抱歉今天再次挑剔你,但是虽然这个答案是“技术上正确,最好的一种正确”,因此似乎受到语言律师的青睐,但它并没有真正回答问题。标准库的实现者非常聪明,不会无缘无故浪费空间。真正的原因是对齐。
  • @NirFriedman,我说了什么?由于填充(出于对齐目的)和需要容纳额外的成员,大小如果更大。
  • 好吧,你没有提到对齐这个词(这是最重要的一个词),此外,我不认为 OP 提到的变体的大小是实际上是绝对最小的。
  • @NirFriedman,填充和对齐是相互的,就像汉堡是番茄酱一样。如果您搜索填充:google.com/…,最上面的结果是data structure alignment
【解决方案4】:

有趣的是,你被巧合欺骗了。下面的返回是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 个字节。这是出于对齐的原因。
  • @NirFriedman 当然。只是它保留了那个数量。我会更正我的说法。
  • 为什么要猜测是否可以查看标题。通过阅读标准库的标题,我学到了很多(坏事)。当然是实现定义的
猜你喜欢
  • 2013-05-20
  • 1970-01-01
  • 2017-07-15
  • 1970-01-01
  • 2016-09-09
  • 2018-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多