C++ ranges find and return std::optional like Java stream
C++ ranges find and return std::optional like Java stream
在Java、Stream.findFirst()
returns Optional<T>
. I'd like to have the similar behavior for std::ranges::find()
。如果找不到该值,则使用 returns last
迭代器。如果 T
是一个结构并且我试图从中获取一个成员,这会很不方便。这是一个演示代码:
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
当然,我可以在每个 find
周围放置一些包装代码来转换迭代器,但这会使代码变得冗长和样板化。我想知道 C++20 中是否有一些更惯用的方法来做到这一点?
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";
// ^^^^^^^^
}
}
这是我自己编写通用包装器的解决方案。与Sprite的版本相比,我的版本returns成员变量而不是迭代器。
template<std::ranges::input_range R, class T, class ProjFind = std::identity, class ProjOut = std::identity>
requires std::indirect_binary_predicate<std::ranges::equal_to,
std::projected<std::ranges::iterator_t<R>, ProjFind>,
const T *>
auto optionalFind( R&& r, const T& value, ProjFind projFind = {}, ProjOut projOut = {} ) {
const auto iter = std::ranges::find(r, value, projFind);
return iter == r.end() ? std::nullopt : std::optional(std::invoke(projOut, *iter));
}
并与
一起使用
std::optional<int> height2 = optionalFind(people, 26, &Person::age, &Person::height);
或
std::optional<Person> person = optionalFind(people, 26, &Person::age);
在Java、Stream.findFirst()
returns Optional<T>
. I'd like to have the similar behavior for std::ranges::find()
。如果找不到该值,则使用 returns last
迭代器。如果 T
是一个结构并且我试图从中获取一个成员,这会很不方便。这是一个演示代码:
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
当然,我可以在每个 find
周围放置一些包装代码来转换迭代器,但这会使代码变得冗长和样板化。我想知道 C++20 中是否有一些更惯用的方法来做到这一点?
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";
// ^^^^^^^^
}
}
这是我自己编写通用包装器的解决方案。与Sprite的版本相比,我的版本returns成员变量而不是迭代器。
template<std::ranges::input_range R, class T, class ProjFind = std::identity, class ProjOut = std::identity>
requires std::indirect_binary_predicate<std::ranges::equal_to,
std::projected<std::ranges::iterator_t<R>, ProjFind>,
const T *>
auto optionalFind( R&& r, const T& value, ProjFind projFind = {}, ProjOut projOut = {} ) {
const auto iter = std::ranges::find(r, value, projFind);
return iter == r.end() ? std::nullopt : std::optional(std::invoke(projOut, *iter));
}
并与
一起使用std::optional<int> height2 = optionalFind(people, 26, &Person::age, &Person::height);
或
std::optional<Person> person = optionalFind(people, 26, &Person::age);