What should I use instead of void as one of the alternative types in an variant?
Asked Answered
C

1

13

I want to have a variant which may contain type Foo, (disjoint) type Bar, or nothing. Well, naturally, I was thinking of using std::variant<Foo, Bar, void> - but this doesn't seem to work. That is, you can define this type, but if you try to instantiate this you'll fail (GCC 8.2).

So what do I use instead? Some kind of empty struct?

Carltoncarly answered 2/11, 2018 at 22:24 Comment(0)
C
25

What you really want is a type among the alternatives which has a single possible value - not void, which has no possible values (and is problematic in other ways). In other words: A unit type instead of a bottom type.

The standard library has defined, as part of <variant>, a "unit type" for this use case: std::monostate (and yes, it's essentially an empty struct). Use it.

Example:

#include <variant>

using Foo = int;
using Bar = double;

int main() {
    std::variant<std::monostate, Foo, Bar> v; 
    v = Foo{}; 
}

Note that, unlike in the question, the single-possible-value type is the first alternative; this allows the variant to be default-constructible even if Foo isn't. Also, it's potentially cheaper/faster to construct the variant this way than constructing a Foo, even if it is default-constructible.

Carltoncarly answered 2/11, 2018 at 22:24 Comment(6)
struct { }; as replacement of void is not that new. New (for me) is that it got part of std library since C++17 (as std::monostate). ;-)Decoder
Sorry, I didn't mean this offensive - just was surprised after I read about std::monostate. "Something or nothing" reminds me to std::optional but I believe the memory footprint of your std::variant is even lower (although this might depend from implementation details).Decoder
@Scheff: It's not "mine"... :-) Also, I doubt the memory footprint of std::variant<std::monotone, T> is lower than std::optional<T>...Carltoncarly
I once digged into code of std::optional (out of curiosity) and I believe it stores aside of pay-load whether it was constructed or not (but it's a while ago - I might fail). I assume, std::variant doesn't need such extra "admin", does it? Out of curiosity, I took your std::variant<std::monostate, Foo, Bar> and compared it with my alternative std::optional<std::variant<Foo, Bar> > which I wouldn't prefer: Live Demo on coliru. (I already disclaimed that implementations of std classes may differ.)Decoder
std::variant needs to store the index of which type currently occupies the variant (and to allow for no-type for the case of exceptions).Carltoncarly
A, yepp. Therefore, the 16 bytes storage. I just was guessing why it needs more than 8 bytes assuming that 8 bytes would be sufficient for int as well as double. The std::monostate consumed 1 byte (I tested before). I once read that sizeof (struct { }) may not be 0. The reason explained was that everything, which can have storage may not have 0 size (if I remember right).Decoder

© 2022 - 2024 — McMap. All rights reserved.