【问题标题】:How is the memory layout of a std::vector of std::variant?std::variant 的 std::vector 的内存布局如何?
【发布时间】:2019-03-03 13:31:47
【问题描述】:

我在堆栈中分配了std::variant 类型的std::vector。由于每个变体的大小都是可变的。我想知道向量的内存布局在堆栈中是什么。

【问题讨论】:

  • 由于每个变体的大小都是可变的。 这是错误的。变体将与其最大类型一样大或更大。
  • 对象的大小是恒定的。 T 类型的对象将在编译时确定一定的大小,T 的所有实例将具有完全相同的大小。如果一个对象似乎改变了大小,它并没有改变,而是额外的数据存储在其他地方动态分配的内存中,而不是大小的一部分。
  • Also ...vector is in stack..."栈上的vector只是一个指针和一个大小(或2个指针)。vector中包含的数据在堆上。
  • 变体的大小不会改变。变体大到足以容纳任何变体类型。但是变体的值表示的大小可以改变。不参与值表示的额外位称为填充位。变量向量可能会填充很多填充位。向量的变体将具有更少的填充位。

标签: c++ memory-layout std-variant


【解决方案1】:

C++ "std:variant" 类型正在模仿良好的旧 C "union" 类型(或者,更接近地,pascal 标记记录),这意味着它们都具有相同的大小,唯一的区别是 std: :variant 值具有与之关联的附加信息,此信息跟踪变体替代项。 std::variant 的确切实现是特定于平台的,恐怕是不可移植的。

std::variant 的 Visual C++ 实现非常复杂(大约 86 KB 的元模板代码)。但是我们可以通过简单的测试来猜测一些实现细节:

#include <stdio.h>
#include <cstdint>
#include <variant>

template <typename T>
void Dump(T val)
{
    printf("Size %zu: Data:",sizeof(val));
    for (int i = 0; i < sizeof(val); ++i) printf(" %02X", (reinterpret_cast<std::uint8_t*>(&val))[i]);
    printf("\n");
}

#pragma pack(push, 1)
typedef struct { std::variant<std::uint32_t, std::uint64_t> u; } dummy_variant_t;
#pragma pack(pop)

int main(int, char*[])
{
    dummy_variant_t abc;

    //                _______________________ __ ?? ?? ?? ?? ?? ?? ?? <-- unknown info
    // Size 16: Data: EF CD AB 89 67 45 23 01 01 13 EC 00 02 00 00 00
    //                ^variant data           ^tag(uint64_t)
    abc.u = static_cast<std::uint64_t>(0x123456789ABCDEF);
    Dump(abc);

    //                ___________ xx xx xx xx __ ?? ?? ?? ?? ?? ?? ?? <-- unknown info
    // Size 16: Data: 78 56 34 12 67 45 23 01 00 13 EC 00 02 00 00 00
    //                ^           ^garbage    ^tag(uint32_t)
    //                |
    //                +variant data
    abc.u = static_cast<std::uint32_t>(0x12345678);
    Dump(abc);

    return 0;
}

这里我们看到这个具体的std::variant类型大致对应:

struct variant_t
{
    union
    {
        std::uint32_t m_Variant1;
        std::uint64_t m_Variant2;
    }
    m_VariantData;
    std::uint8_t m_Tag;
    std::uint8_t m_Unknown[7];
};

所以,我希望这有助于确定您的确切类型。 Pascal 很久以前就有这些类型(几乎从一开始),请参阅excerpt from the freepascal manual

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-21
    • 2017-02-01
    • 1970-01-01
    • 2018-12-13
    • 2020-05-21
    • 1970-01-01
    • 2011-01-19
    • 2018-10-23
    相关资源
    最近更新 更多