Search code examples
c++c++20stdoptional

C++ ranges find and return std::optional like Java stream


In Java, Stream.findFirst() returns Optional<T>. I'd like to have the similar behavior for std::ranges::find(). If the value is not found, it returns last iterator. This is inconvenient if T is a struct and I try to get a member out of it. Here is a demonstration code:

    struct Person {
        int age;
        int height;
    };

    std::vector<Person> people;
    people.emplace_back(20, 100);
    people.emplace_back(23, 170);

    // find the height of a 23yo person
    std::optional<int> height1 = std::ranges::find(people, 23, &Person::age)->height;

    // find the height of a 26yo person
    std::optional<int> height2 = std::ranges::find(people, 26, &Person::age)->height;  // error

Of course I can put some wrapper code around each find to transform the iterator, but it makes the code so verbose and boilplate-y. I wonder if there's some more idiomatic way in C++20 to do this?

    std::optional<int> height2;
    auto iter = std::ranges::find(people, 26, &Person::age);
    if (iter == people.end()) {
        height2 = std::nullopt;
    } else {
        height2 = iter->height;
    }

Solution

  • template <class ...ArgsT>
    auto OpRangesFind(auto &&Range, ArgsT &&...Args)
    {
        auto Iter = std::ranges::find(Range, std::forward<ArgsT>(Args)...);
        return Iter != Range.end() ? Iter : std::optional<decltype(Iter)>{std::nullopt};
    }
    
    int main()
    {
        /* ... */
    
        auto OpIter = OpRangesFind(people, 26, &Person::age);
        if (!OpIter.has_value()) {
            std::cout << "not found\n";
        } else {
            std::cout << "found\n";
            std::cout << "height: " << OpIter.value()->height << "\n";
            //                               ^^^^^^^^
        }
    }