C++ 范围像 Java 流一样查找并返回 std::optional
Posted
技术标签:
【中文标题】C++ 范围像 Java 流一样查找并返回 std::optional【英文标题】:C++ ranges find and return std::optional like Java stream 【发布时间】:2021-03-30 17:10:00 【问题描述】:在 Java 中,Stream.findFirst()
returns Optional<T>
。我希望std::ranges::find()
有类似的行为。如果未找到该值,则返回 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;
【问题讨论】:
您始终可以编写一个包装函数来封装.end()
的检查。无需到处复制代码。
考虑一下,我实际上想要一个包装器,1) 从 borrowed_iterator<T>
转换为 std::optional<T>
,以及 2) 允许投影从 T
中提取成员。您能否在回答中提供此类包装器的模板示例?
Anyway, op*
and op->
simply assume they are used correctly on pain of UB. 是的,!result
比 result == std::end(container)
更方便一点,但这与直接使用迭代器相平衡。此外,可选的迭代器通常比简单的迭代器大...
【参考方案1】:
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";
// ^^^^^^^^
【讨论】:
OpIter
不是 dangling
的可选项,而不是 Person
?
@CrendKing 嗯,是的,有一个错误,我会尝试修复它。
@CrendKing 已编辑,您能再检查一下吗?
谢谢。有用。我对其进行了一些更改以满足我的需要(返回成员变量而不是迭代器)并在答案中提供。感谢您的帮助。【参考方案2】:
这是我自己编写通用包装器的解决方案。与 Sprite 的版本相比,我的版本返回的是成员变量而不是迭代器。
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);
【讨论】:
以上是关于C++ 范围像 Java 流一样查找并返回 std::optional的主要内容,如果未能解决你的问题,请参考以下文章
使用std :: equal_range查找字符串向量中出现的前缀范围