How do I tell if a std::variant holds any value at all?
Asked Answered
A

2

9

I already know how to use std::variant fairly well with std::get_if(), std::get() and std::visit(). But, what if I just want a simple way to tell if a variant has ever been initialized to any value? That is, I don't care what the value is, I just want a boolean test. How do I do that?

For example, suppose I declare a variant on the stack:

std::variant<int, double> data;

Then, my function goes on and might or might not initialize this variable. At the end of my function, I want to test if it was initialized.

  • I looked at the index() function. That returns 0 both for an uninitialized variant and for one I initialize to the first type declared.
  • I looked at the valueless_by_exception() function but that returns false whether I initialize the variant or not.

About the only thing I could think of to compare it to a default constructed one, like this:

using Data = std::variant<int, double>;

Data data;

// ... code here that might initialize data or might not...

if (data == Data())
    // Not initialized
else
    // Initialized

This seems to work, but reading the comments on operator==() for std::variant, it seems like this behavior is undefined.

So, is this a safe way to test, or is there another way?

Ape answered 28/7, 2023 at 22:40 Comment(0)
G
17
std::variant<int, double> data;

data is initialized. A std::variant is always initialized with one of its values, unless it's valueless_by_exception().

Unless a std::variant instance has an explicit constructor, the first variant alternative gets default-constructed. This specific data holds an int value, that's default-constructed.

If the first std::variant value does not have a default constructor than the variant cannot be default-constructed, and must have an explicit constructor.

When it is desired to have a concept of a variant that doesn't "really" (with the word "really" in air-quotes) have a default value: the usual convention is to have std::monostate as the first variant value. This is what std::monostate is for.

std::variant<std::monostate, int, double> data;

This variant is default-constructed and holds a std::monostate value. Its index() is 0. You can consider this variant to be uninitialized, whatever that actually means to your application, when your variant has a 0 index.

There's nothing special about std::monostate itself. It's just an empty class. If you visit a variant with a std::monostate value your visitor needs to deal with a std::monostate alternative.

Graniah answered 28/7, 2023 at 22:52 Comment(0)
M
4

As with anything that needs a separate "no value" state, you can use std::optional:

std::optional<std::variant<int, double>> data{}; // doesn't have a value
// ...
if (data.has_value()) {
   // ...
}
Muttonhead answered 28/7, 2023 at 23:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.