Skip to main content

std::optional and std::variant

intermediate30 min readLesson 104 of 204

Absence without sentinels, sum types with visit, and honest signatures for parse-style results.

std::optional<T> is a T that may be absent โ€” the vocabulary type for "maybe no value" without sentinel magic (-1, nullptr, empty string).

#include <optional>

std::optional<int> find_index(const std::vector<int>& v, int target) {
    for (std::size_t i = 0; i < v.size(); ++i)
        if (v[i] == target) return static_cast<int>(i);
    return std::nullopt;
}

auto idx = find_index(v, 7);
if (idx.has_value()) use(*idx);      // or idx.value() (throws if empty)
int fallback = idx.value_or(-1);     // read with a default

std::variant<A, B> is a type-safe union: it holds exactly one of the alternatives, and std::visit dispatches on which one:

#include <variant>

std::variant<int, std::string> result = 42;
result = std::string{"overflow"};

auto text = std::visit([](auto&& x) {
    using T = std::decay_t<decltype(x)>;
    if constexpr (std::is_same_v<T, int>) return std::to_string(x);
    else return x;
}, result);

Choose optional when the question is "is there a value?"; choose variant when the answer is "one of several different types" โ€” parse results, state machines, tagged outputs. Both replace error-channel out-parameters and make signatures honest.

Now practice

variant practiceParse positive integers into a value-or-error variant.1 challenge ยท ยท ~30 min