4

cppreference states:

The ranges library is an extension and generalization of the algorithms and iterator libraries that makes them more powerful by making them composable and less error-prone.

The library creates and manipulates range views, lightweight objects that indirectly represent iterable sequences (ranges).

It mentions using range views, which are, as cppreference states:

The range concept defines the requirements of a type that allows iteration over its elements by providing an iterator and sentinel that denote the elements of the range.

But from an outside perspective, it just seems like a wrapper of an iterator with a concept. So the main question is:

  • What are the problems with using regular iterators that the ranges library solves (code examples will be appreciated), and when should you use it?
dnl
  • 95
  • 7
  • You can get more naturally looking syntax, e.g. from the linked cppreference page: `return std::forward(r) | std::views::take(3) | std::views::reverse;`, `for (int i : ints | std::views::filter(even) | std::views::transform(square))`. But obviously since range is just a pair of iterators you can always get the same result with regular functions taking a pair of iterators. – dewaffled Mar 10 '23 at 11:06
  • _except for the slightly shorter form_ and the fact that, as there is less to type, there is less chance of making an error. – Paul Sanders Mar 10 '23 at 11:32
  • 1
    ranges algorithm have concept, so provide generally better error messages when misused. the concept are generally stronger than the one implicitly expected from iterator alternative (whereas `operator <` is enough for iterator version, ranges version might require totally_ordered concept (so <, >, <=, >=, ==, !=)) – Jarod42 Mar 10 '23 at 13:12
  • 2
    [This question is being discussed on Meta](https://meta.stackoverflow.com/questions/423862) – TylerH Mar 30 '23 at 21:32

2 Answers2

6

The second one will allow you to pipe other operations present only in the ranges header, which is much better for composability:

#include <ranges>
#include <vector>
#include <algorithm>
#include <iostream>

int main()
{
    const auto v = std::vector{ 1, 2, 3, 4, 5 };
    const auto is_odd = [](const auto n){ return n % 2 == 1; };
    const auto square = [](const auto n){ return n * n; };
    const auto print = [](const auto n){ std::cout << n << " ";};
    std::ranges::for_each(
        v | std::ranges::views::filter(is_odd) | std::ranges::views::transform(square),
        print); // prints: 1 9 25 
}
thebugger
  • 123
  • 6
  • 1
    To be fair you can also use pipes with normal `std::for_each`, but you'd have to create another temporary variable. It's just so much easier to write with `std::ranges::for_each`. – Lukas-T Mar 10 '23 at 12:59
  • @ago that's true, you're right :) – thebugger Mar 10 '23 at 14:46
3

what are the problems with using regular iterators that ranges library solves (code examples will be appreciated)?

It might avoid some mistake with temporary container:

// assuming std::vector<int> compute_vector();

// Following is wrong begin and end refers to different (temporary) containers
const bool KO = std::is_sorted(compute_vector().begin(), compute_vector().end()); // WRONG

// Here all is ok.
const bool OK = std::ranges::is_sorted(compute_vector());
Jarod42
  • 203,559
  • 14
  • 181
  • 302