by klmr

Range-based for loops to iterate over a range of numbers or values

232 Stars 28 Forks Last release: Not found Other 16 Commits 0 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

Re-imagining the

C++11 now knows two distinct types of

loops: the classic loop over an “index” and the range-based
loop which vastly simplifies the iteration over a range specified by a pair of iterators.

By contrast, Python knows only one loop type – roughly equivalent to the range-based for loop. In fact, loops over indices are exceedingly rare, but made possible by the use of the

for i in range(10):
    print i

Which does what it promises – although Python version < 3.0 does the “wrong” thing and actually instantiates the whole collection in memory at once; a remedy is

which yields values lazily as they are consumed by the loop.

C++11 effortlessly allows the same but there is no standard library function to provide this. Boost.Range provides part of the functionality via

which only works on integers, and not for unlimited ranges (this will make sense in a second).

The header

provides a very basic implementation for this. It allows running the following code:
for (auto i : range(1, 5))
    cout << i << "\n";

for (auto u : range(0u)) if (u == 3u) break; else cout << u << "\n";

for (auto c : range('a', 'd')) cout << c << "\n";

for (auto i : range(100).step(-3)) if (i < 90) break; else cout << i << "\n";

with a single argument deviates from the Python semantic and creates an endless loop, unless it’s interrupted manually. This is an interesting use-case that cannot be modelled in Python using

Iterating over container indices

In Python, the one-argument version of

is often used to iterate over the indices of a container via
. Because that overload creates an infinite range in our C++ library, we cannot use this idiom.

But we can do better anyway. For those few cases where we actually want to iterate over a container’s indices, we just use the

std::vector x{1, 2, 3};
for (auto i : indices(x))
    cout << i << '\n';

This works as expected for any type which has a member function

size() const
that returns some integral type. It also works with
s and C-style fixed-size arrays.1


to the end of either
specifies a step size instead of the default, 1.

The construct works for arbitrary types which fulfil the interface requirements (incrementing, copying, equality comparison, default construction in the case of infinite ranges).

1 This includes string literals, which are C-style strings that include null termination; this may lead to surprising results, because

results in 0, 1, 2, 3, 4, whereas
results in 0, 1, 2, 3.

Performance (the cost of beauty)

When compiling with optimisations enabled (and why wouldn’t you?), using the

function yield very similar output compared with a manual
loop. In fact, on g++ 4.8 with
or higher, the following two loops yield identical assembly.
for (int i = 0; i < n; ++i)
    cout << i;

for (int i : range(0, n)) cout << i;

Even though the

function creates a proxy container and an iterator wrapper, those are completely elided from the resulting code.

☞ Beauty is free.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.