Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating over std::optional

I tried to iterate over an std::optional:

for (auto x : optionalValue)
{
    ...
}

With the expectation of it doing nothing if optionalValue is empty but doing one iteration if there is a value within, like it would work in Haskell (which arguably made std::optional trendy):

forM optionalValue
( \x ->
    ...
)

Why can't I iterate an optional? Is there another more standard C++ method to do this?

like image 872
yairchu Avatar asked Sep 12 '25 06:09

yairchu


2 Answers

std::optional does not have a begin() and end() pair. So you cannot range-based-for over it. Instead just use an if conditional.

See Alternative 2 for the closest thing to what you want to do.

Edit: If you have a temporary from a call result you don't have to check it explicitly:

if (auto const opt = function_call()) {
  do_smth(*opt);
}

The check for static_cast<bool>(opt) is done implicitly by if.


Alternative 1

Another alternative is to not use an std::optional<T> but std::variant<std::monostate, T>. You can then either use the overloaded idiom or create a custom type to handle the monostate:

template <typename F>
struct MaybeDo {
  F f;

  void operator()(std::monostate) const {}
  template <typename T>
  void operator()(T const& t) const { f(t); }
};

which will allow you to visit the value with some function:

std::variant<std::monostate, int> const opt = 7;
std::visit(MaybeDo{[](int i) { std::cout << i << "\n"; }}, opt);

Alternative 2

You can also wrap optional in a thing that allows you to iterate over it. Idea:

template <typename T>
struct IterateOpt {
  std::optional<T> const& opt;
  
  struct It {
    std::optional<T> const* p;
    It& operator++() {
      p = nullptr;
      return *this;
    }
    It operator++(int) {
      return It{nullptr};
    }
    auto const& operator*() const { **p; }
  };
  auto begin() const {
    if (opt) return It{&opt};
    else end();
  }
  auto end() const {
    return It{nullptr};
  }
};

This is a crude sketch of how to do this and probably requires some love to work on different situations (like a non-const optional).

You can use this to "iterate" over the optional:

for (auto const& v: IterateOpt{function_call()}) {
  do_smth(v);
)
like image 63
bitmask Avatar answered Sep 13 '25 21:09

bitmask


There are good news, Proposal P3168R2 Give std::optional Range Support was accepted into C++26 in 2024-06 in St. Louis which "magically" makes your code compile and work.

This is achieved by making optional a view (by specializing std::ranges::enable_view<std::optional<T>> to true) and by adding member begin() and end() functions which return a dedicated but simple iterator type.

There are already two implementations that can be used until the advent of C++26:

  • Optional26 of the Beman project (additionally supports std::optional<T &>)
  • owi::optional optional with iterators

Notes:

  • cppreference already lists the new member functions begin() and end().
  • optional.iterators on eel.is now contains the accepted wording for iteration support
like image 31
Brandlingo Avatar answered Sep 13 '25 21:09

Brandlingo