C++20 ranges如何遍历已排序范围的equal_range等值区间?
解决方案
C20标准库没有内置直接满足需求的equal_range_split适配器,但可以自行实现符合C20 ranges规范的自定义视图适配器,完美支持管道式组合调用。
实现代码
#include <vector> #include <ranges> #include <iostream> #include <algorithm> #include <fmt/ranges.h> template<std::ranges::forward_range R, typename Comp = std::ranges::equal_to> requires std::ranges::view<R> struct equal_range_split_view : std::ranges::view_interface<equal_range_split_view<R, Comp>> { private: R base_; Comp comp_; std::ranges::iterator_t<R> current_ = std::ranges::begin(base_); struct iterator { using value_type = std::ranges::subrange<std::ranges::iterator_t<R>>; using difference_type = std::ranges::range_difference_t<R>; equal_range_split_view* parent_ = nullptr; value_type operator*() const { return std::ranges::equal_range(parent_->current_, std::ranges::end(parent_->base_), *parent_->current_, parent_->comp_); } iterator& operator++() { parent_->current_ = std::ranges::upper_bound(parent_->current_, std::ranges::end(parent_->base_), *parent_->current_, parent_->comp_); return *this; } iterator operator++(int) { auto tmp = *this; ++*this; return tmp; } bool operator==(const iterator& other) const { return parent_->current_ == other.parent_->current_; } bool operator==(std::ranges::default_sentinel_t) const { return parent_->current_ == std::ranges::end(parent_->base_); } }; public: equal_range_split_view() = default; equal_range_split_view(R base, Comp comp = Comp{}) : base_(std::move(base)), comp_(std::move(comp)), current_(std::ranges::begin(base_)) {} iterator begin() { return {this}; } std::ranges::default_sentinel_t end() const { return {}; } }; // 适配器闭包实现,支持管道调用 namespace detail { struct equal_range_split_fn { template<std::ranges::forward_range R> auto operator()(R&& r) const { return equal_range_split_view(std::views::all(std::forward<R>(r))); } template<std::ranges::forward_range R, typename Comp> auto operator()(R&& r, Comp comp) const { return equal_range_split_view(std::views::all(std::forward<R>(r)), std::move(comp)); } template<typename Comp = std::ranges::equal_to> auto operator()(Comp comp = {}) const { return std::ranges::range_adaptor_closure([comp = std::move(comp)]<std::ranges::forward_range R>(R&& r) { return equal_range_split_view(std::views::all(std::forward<R>(r)), comp); }); } }; } inline constexpr detail::equal_range_split_fn equal_range_split{}; // 测试代码 int main() { std::vector vals {1,2,2,3,3,3,47}; // 完全符合期望的调用风格 vals | equal_range_split | std::ranges::for_each([](const auto& eq_range) { std::cout << "size: " << eq_range.size() << "\n"; std::cout << fmt::format("{}\n", eq_range); }); }
特性说明
- 完全符合C++20 ranges规范,支持任意前向迭代器的已排序范围,惰性求值,无额外拷贝开销
- 支持自定义比较谓词,针对自定义类型排序的场景,可传入自定义比较函数:
vals | equal_range_split(std::greater{}) | ... - 具备完全的可组合性,可与其他标准库视图适配器链式调用,后续可接
std::views::filter、std::views::transform等实现更复杂的逻辑
内容的提问来源于stack exchange,提问作者NoSenseEtAl
相关产品推荐
相关产品推荐

