Search code examples
c++c++20std-variant

Why do std::variant implementations take more than 1 byte for variant of empty types?


This is mostly trivia question, as I doubt that I will ever need this space saving.

While playing around on godbolt I noticed that both libstdc++ and libc++ implementations of std::variant require more than 1 byte to store variant of empty structs.

libstc++ uses 2 bytes

libc++ uses 8 bytes

I presume it is just not worth the trouble to optimize this, but I wonder if there is any other reason. In particular is there something in standard wording for std::variant that prevents this optimization.


Solution

  • Every object takes up at least 1 byte of space. The counter itself needs to take up at least 1 byte, but you also need space for the potential choices of object. Even if you use a union, it still needs to be one byte. And it can't be the same byte as the counter.

    Now, you might think that no_unique_address could just come to the rescue, permitting the member union to overlap with the counter if all of the union elements are empty. But consider this code:

    empty_type e{};
    variant<empty_type> ve{in_place_index<0>}; //variant now stores the empty type.
    auto *pve = ve.get_if<0>(); //Pointer to an `empty_type`.
    memcpy(pve, &e, sizeof(empty_type)); //Copying from one trivial object to another.
    

    The standard does not say that the members of a variant are "potentially-overlapping subojects" of variant or any of its internal members. Therefore, it is 100% OK for a user to do a memcpy from one trivial empty object to the other.

    Which will overwrite the counter if it were overlapping with it. Therefore, it cannot be overlapping with it.