C++11 Range-based for loops
for loop is one of the oldest control flow control constructs
in the Algol family of languages. Yet while other languages have extended
their syntax to allow
for loops to do all sorts of crazy and useful things
beyond iterate over a range of numbers, C and C++ have remained steadfast -
for loop finally has a new syntax to better support iterators and
ranges, just two great new features in C++11. So you can now easily iterate
over much more than just numbers.
The C++ container classes in the Standard Template Library (STL) provide iterators, but the familiar looping syntax is the rather unwieldly pattern:
For some time, the Boost++ project has provided
some syntactic sugar to reduce the complexity of this code considerably. By
using the Boost
foreach library, you
can replace the above loop with the much simpler:
But now the C++11 specification finally has this style of syntax built in to
the language (with a slight change of punctuation; it uses
: rather than
,). So now you can write:
This is obviously much cleaner and clearer than the explicit iterator-based code shown above. If all you need to do is iterate over an STL container, you can start using this syntax straight away.
(Aside: if you are actually going to sum the contents of a vector as in this
example, using the
reduce algorithm is even better.)
for loop in detail
So how does this new range-based
loop actually work? Well, given the simple expression:
this loop is equivalent to the following expanded code:
Stepping through line by line, we see that the
for loop lives inside its
own block. First,
__range is evaluated to determine the sequence over
which to iterate. Then (as in the traditional container case) the
__end variables are used to control the loop, incrementing
until it reaches
__end (which is one past the last entry). The loop body
dereferences the iterator to get the item value at the current position, and
then the code in
loop_statement is executed.
This construct also works naturally with plain arrays, where the bound is added to the beginning to determine the range.
Finally, this works with the new
begin()/end() range functions in the
C++11 standard library, described next.
Iterators defined by containers in the STL have
methods to control iteration loops. New in C++11 are
functions (in the
std namespace), which can be used to work with ranges.
So what is a range? Essentially a
begin()/end() pair which define the
extents of an iteration over a container. By making
functions, generic algorithms can more easily work with user-defined
containers that live outside the STL.
The simple form of their declarations are as follows:
This uses some new C++11 features to basically say that, for whatever type
you pass to these functions, they will return you something of the same type
end() methods they define within the class. This
works with C style arrays, initialiser lists, STL containers and any
user-defined types. In short, it is a much more flexible form of the
original STL pattern.
Custom classes and ranges
To add range support to your own class, you must provide the following support:
end(), either as methods or overloaded functions, that return the appropriate iterators
and then you need an iterator class with the following features:
- dereferencing to access the current item (
- inequality to compare iterators (
- pre-increment to advance the iterator to the next item (
Given a container class, you could define your iterator class something like this, for example:
Its responsibilities are primarily to keep a reference to its container
proper, and maintain the index of the current item. The methods marked
required are the minimum interface to participate in the range-based
loop support. The implementation is very straightforward.
The container class must provide methods to generate iterator classes; something like this:
This allows your own custom classes to be used in a very natural way with
for loop, such as this example:
The full source to the examples is available in my Github C++11 samples.