10

If I have range (pair of 2 iterators) is there a way to write "for each" loop for that uses range, not a raw array or container.

Something like this:

auto rng = std::equal_range(v.begin(),v.end(),1984);
for(const auto& elem: rng) {
    // ...
}
Jacob Parker
  • 2,546
  • 18
  • 31
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277

4 Answers4

15

As per Why was pair range access removed from C++11? you can use an adaptor e.g. the as_range at the accepted answer, boost::make_iterator_range, or write your own:

template<typename It> struct range {
   It begin_, end_;
   It begin() const { return begin_; }
   It end() const { return end_; }
};
template<typename It> range<It> as_range(const std::pair<It, It> &p) {
   return {p.first, p.second};
}

auto rng = std::equal_range(v.begin(),v.end(),1984);
for(const auto& elem: as_range(rng))
    ...

The reason this isn't applicable in general is that per Alastair Meredith's paper, of the algorithms,

  • mismatch and partition_copy return a pair of iterators from different ranges;
  • minmax returns a pair of objects that may not be iterators at all, and if they are there's no guarantee they form a range;
  • minmax_element can return a range, but it can also return a reversed range (e.g. on a reverse-sorted range minmax_element will return {prev(last), first};
  • equal_range is guaranteed to return a range.
Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
3

I don't think it will work like that out of the box as equal_range returns a pair of iterators while, the for cycle over range according to documentation are:

The begin_expr and end_expr are defined to be either:
If (__range) is an array, then (__range) and (__range + __bound), where __bound is the array bound
If (__range) is a class and has either a begin or end member (or both), then begin_expr is __range.begin() and end_expr is __range.end();
Otherwise, begin(__range) and end(__range), which are found based on argument-dependent lookup rules with std as an associated namespace.

I would say you may define begin and end functions that take the pair of iterators and return first and second one resepectively.

Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
1
#include <vector>
#include <algorithm>
#include <iostream>

template <typename I>
struct range_adapter {
    std::pair<I, I> p;

    range_adapter(const std::pair<I, I> &p) : p(p) {}

    I begin() const { return p.first; }
    I end() const { return p.second; }
};

template <typename I>
range_adapter<I> in_range(const std::pair<I, I> &p)
{
    return range_adapter<I>(p);
}

int main()
{
    std::vector<int> data { 1, 2, 2, 3, 3, 3, 4 };

    auto r = std::equal_range(data.begin(), data.end(), 2);

    for (const auto &elem : in_range(r))
    {
        std::cout << elem << std::endl;
    }
}
0

What std::equal_range returns is simply a std::pair. The standard doesn't cover any method to iterate over such things.

What you may want to read is Alexandrescu's "Iterators must go" presentation. Here is the video. An excellent read on a more elegant way to iterate containers using Ranges.

Ranges are implemented in his Loki library.

Ed Rowlett-Barbu
  • 1,611
  • 10
  • 27
  • ranges are implemented in boost and they are on their way to std, so anybody who wants to use them should use boost – NoSenseEtAl Mar 20 '13 at 18:21
  • I am aware of Boost.Range however I didn't take quite a good look at it, wasn't sure if they're the same as Alexandrescu's Ranges. Which is why I didn't suggest Boost.Range. Are you sure they're the same thing? – Ed Rowlett-Barbu Mar 20 '13 at 18:22
  • afaik no, but idk the difference, if iirc corectly boost/std ranges are just pairs of iters.... but i really dont know the details... – NoSenseEtAl Mar 20 '13 at 18:41