A c++20 range adaptor whose produced values are iterators to the underlying range

88 Views Asked by At

I want to have the following work

std::vector<int> range{0,1,2,3};

for(std::vector<int>::iterator vit : range | iterator_range  ){
   int v = *vit;
}

this would be equivalent to the standard for loop

std::vector<int> range{0,1,2,3};

for(std::vector<int>::iterator vit = begin(range); vit !=end(range); vit++)  ){
   int v = *vit;
}

except more elegant.

iterator_range is an adapter that I require.

I have some algorithms where it is useful to require to have an iterator to the element inside the loop rather than the element itself.

2

There are 2 best solutions below

0
Barry On BEST ANSWER

Note that views::iota is not limited to integers, it's anything that's incrementable and comparable. Which includes iterators!

inline constexpr auto iterators_of = []<ranges::range R>(R&& r){
    static_assert(ranges::borrowed_range<R>);
    return views::iota(ranges::begin(r), ranges::end(r));
};

This at least gets you started, and maybe is good enough. Ideally you have a proper range adapter that captures views::all(r) without requiring borrowed, and actually gives out these iterators directly.

A more complete implementation (if actually necessary) would be:

template <std::ranges::view V>
struct iterators_for_view : std::ranges::view_interface<iterators_for_view<V>> {
    V view;

    template <class R>
    using iota_for = std::ranges::iota_view<std::ranges::iterator_t<R>, std::ranges::sentinel_t<R>>;

    explicit iterators_for_view(V v) : view(std::move(v)) { }

    auto begin() { return std::ranges::iterator_t<iota_for<V>>(std::ranges::begin(view)); }
    auto end() { return std::ranges::sentinel_t<iota_for<V>>(std::ranges::end(view)); }

    auto begin() const requires std::ranges::range<V const> { return std::ranges::iterator_t<iota_for<V const>>(std::ranges::begin(view)); }
    auto end() const requires std::ranges::range<V const> { return std::ranges::sentinel_t<iota_for<V const>>(std::ranges::end(view)); }    

    auto size() requires std::ranges::sized_range<V> { return std::ranges::size(view); }
    auto size() const requires std::ranges::sized_range<V const> { return std::ranges::size(view); }
};

struct iterators_for_fn : std::ranges::range_adaptor_closure<iterators_for_fn> {
    template <std::ranges::viewable_range R>
    constexpr auto operator()(R&& r) const {
        return iterators_for_view(std::views::all((R&&)r));
    }
};

inline constexpr iterators_for_fn iterators_for{};

Which you can see here.

0
康桓瑋 On

Iterator types always satisfy weakly_incrementable, so you can use iota_view:

std::vector<int> range{0,1,2,3};
auto make_iter_range = [](std::ranges::range auto&& r) {
  return std::views::iota(std::ranges::begin(r), std::ranges::end(r));
};
for (auto vit : make_iter_range(range)) {
  int v = *vit;
}