Home > Software engineering >  Iterating over std::optional
Iterating over std::optional

Time:06-30

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?

CodePudding user response:

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);
)
  • Related