Search code examples
c++option-typestdoptional

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?


Solution

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