Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is sizeof(std::variant) the same size as a struct with the same members?

Tags:

c++

c++17

unions

The class template std::variant represents a type-safe union. An instance of std::variant at any given time either holds a value of one of its alternative types, or it holds no value.

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

But if it is a union, why does it take so much space?

struct T1 {
    float a;
    int32_t b;
    double c;
};

struct T2 {
    union {
        float a;
        int32_t b;
        double c;
    };
};

The variant has the same size as the struct

sizeof(T1) == 16
sizeof(T2) == 8

I would expect the size of the union, plus 4 bytes to store, which type is active.

like image 982
Iter Ator Avatar asked Aug 08 '17 18:08

Iter Ator


2 Answers

Here the type with the largest alignment in the variant is double, with an alignment of 8. This means the entire variant must have an alignment of 8. This also means its size must be a multiple of 8, ensuring that in an array each variant will have its double aligned by 8.

But a variant must store more than just the largest type: it must also store an identifier or tag to know which type is currently instantiated. So even if only 1 byte is used for that identifier, the structure as a whole is padded to 16 so that it is a multiple of 8 in size.

A more correct comparison would be:

struct
{
    union
    {
        float a;
        int32_t b;
        double c;
    };
    int identifier;
};
like image 117
Cory Nelson Avatar answered Sep 28 '22 05:09

Cory Nelson


The basic answer is: for alignment reasons. Since one of your types is a double, and doubles are 8 bytes with 8 byte alignment requirements, this means that the variant also has an 8 byte alignment requirement. So it can only be multiples of 8 bytes in size. As you yourself noted, the minimum size is the largest type + something additional to indicate which member is active. That means it cannot fit in 8 bytes, but then alignment requirements force it all the way up to 16 bytes.

That is why it cannot be 12 bytes, as you reason. By the way, there is also no rule that says the active type storage must be 4 bytes. In fact, it's pretty clear that in the vast majority of cases, you can get by with a single byte, which can discriminate between 255 types, plus the empty state. We can test this:

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

I just tested this on coliru, and it printed out "2". In this case, 1 byte for the largest type (bool), and 1 byte to determine which type is active.

like image 20
Nir Friedman Avatar answered Sep 28 '22 03:09

Nir Friedman