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;
}
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";
// ^^^^^^^^
}
}