Existing ranges

Existing sequence can be used as ranges. Any pair of iterators can be converted into a range. But also, standard containers can be used straight away.

Standard containers

To use standard container types as ranges, say:

#include "range/std.hpp"

and then, for example:

using namespace range;

// Initialise vector and tuple.
int elements[] {5, 7, 9};
std::vector <int> v (&elements [0], &elements [3]);
std::tuple <int, std::string, float> t (6, "Hello", 7.5);

// Is the range empty?
assert (!empty (v));
assert (!empty (t));

// Find the size.
assert (size (v) == 3);
assert (size (t) == 3);

// Retrieve the first element.
assert (first (v) == 5);
assert (first (t) == 6);

// Remove one element from the back and then retrieve the next element.
assert (first (drop (v, back), back) == 7);
assert (first (drop (t, back), back) == "Hello");

// Retrieve the element at position 2.
assert (at_c <2> (v) == 9);
assert (at (v, 2) == 9);
assert (at_c <2> (t) == 7.5);


Another type of container is boost::optional, or std::optional, when that comes through. To use this as a range, of length 0 or 1, range::view_optional must be used. For example:

using boost::optional;
using namespace range;

optional <int> none;
optional <int> three (3);

assert (empty (view_optional (none)));

auto three_view = view_optional (three);
assert (!empty (three_view));
assert (size (three_view) == 1);
assert (first (three_view) == 3);
auto const view_optional

View an “optional” as a range containing zero or one elements.

  • optional -

    A boost::optional or std::optional (when that becomes available). This should not be an rvalue, since a reference to the optional is stored.

Pairs of iterators

constexpr auto make_iterator_range

Make an iterator_range from begin and end iterators or from a container. Give either begin and end, or container.

  • begin -

    The begin iterator.

  • end -

    The past-the-end iterator.

  • container -

    The container to iterate. Begin and end iterators are found with unqualified calls to begin and end, after using std::begin and using std::end.

template <class Iterator, class Enable = void>
class range::iterator_range

Range that internally contains two iterators with the same type to denote its begin and end. The type of iterator determines which operations are possible.

There is a sharp difference between an iterator_range based on input iterators on the one hand, and forward iterators or higher on the other hand. An iterator_range based on input iterators can not be copied. Therefore, it does not have a copy constructor; first() and drop() only take an rvalue, as does chop(), which is the most useful operation. An iterator_range based on forward iterators can be copied without any problems.

default_direction (range) just returns front.

empty (range) is always defined.

size (range) is defined only for random-access iterators. The result type of size (range) is the unsigned version of the distance between two iterators.

first (range, front) is defined for all types of iterators; but first (range, back) is defined only for bidirectional iterators.

drop (range, front) is defined for input and forward iterators. drop (range, back) is defined only for bidirectional iterators. drop (range, n, front) and drop (back, n, range) are defined only for random-access iterators.

The code for operations on this class is worth looking at, because it embodies the difference between ranges and iterators. Ranges only shrink, never grow. In terms of the code, this means that begin is only ever incremented, not decremented, and end is only ever decremented. Also, ranges’ ends are known, so it can be asserted that they are non-empty or have at least a certain size for first() and drop(). This can be seen in the assertions.

  • Iterator -

    The underlying iterator type. This must be an input iterator, or a forward iterator or higher. The things called “output iterators” are not supported.

Table Of Contents

Previous topic

Compile-time queries

Next topic

Predefined ranges

This Page