How is the memory layout of a std::vector of std::variant?

1.8k Views Asked by At

I have a std::vector of std::variant types allocated in stack. Since the size of each variant is variable. I am wondering what the memory layout of the vector is in stack.

1

There are 1 best solutions below

0
On

C++ "std:variant" types are mimicking the good old C "union" types (or, more closely, the pascal tagged records), which means that they all have the same size, the only difference is that the std::variant values have additional information associated with them, this information tracks variant alternatives. Exact implementation for std::variant is platform-specific and I am afraid is not portable.

Visual C++ implementation for std::variant is very complicated (roughly 86 Kbytes of meta-template code). But we can guess some implementation details using simple tests:

#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;
}

Here we see that this specific std::variant type roughly corresponds to:

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];
};

So, I hope that this helps to nail your exact type. Pascal had these types long ago (almost from the beginning), see this excerpt from the freepascal manual