11

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?

einpoklum
  • 118,144
  • 57
  • 340
  • 684

1 Answers1

22

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.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • 1
    `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`). ;-) – Scheff's Cat Nov 03 '18 at 08:05
  • 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). – Scheff's Cat Nov 03 '18 at 08:54
  • @Scheff: It's not "mine"... :-) Also, I doubt the memory footprint of `std::variant` is lower than `std::optional`... – einpoklum Nov 03 '18 at 09:01
  • 1
    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` and compared it with _my_ alternative `std::optional >` which I wouldn't prefer: [**Live Demo on coliru**](http://coliru.stacked-crooked.com/a/6f9bbc36471ae6f6). (I already disclaimed that implementations of `std` classes may differ.) – Scheff's Cat Nov 03 '18 at 09:09
  • `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). – einpoklum Nov 03 '18 at 09:15
  • 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). – Scheff's Cat Nov 03 '18 at 09:18