Modern C++ Features – std::begin/end and range based for loops

Here we go again with two features that have been added in C++11 that can greatly simplify the code we write. 

std::begin & Co.

std::begin is a free function template that by default does nothing more than calling a begin member function on its argument and returning the result. Its twin is std::end which, as you might have guessed, does the same with an end member function of the argument.

So, if you have one of the usual standard containers, you can write a loop over the complete container as follows:

At first sight, this may not look like much of an improvement, but we have only just started. There are a few specializations of std::begin and std::end already built into the language, for std::initializer_list, std::valarray and plain C-style arrays, which return suitable iterator-like objects, e.g. T* for arrays of T.

Especially plain arrays are still often used, and they have no begin and end member functions, so in the past a full iteration loop over arrays had to look different than over any standard library container. However, since C++11 you can use the exact same code, making it possible to write template functions that can treat arrays, vectors and the like equally:

Of course, there are also std::rbegin and std::rend for reverse iteration. In addition, since C++14 there are versions that explicitly take the parameter as a constant and return the corresponding const_iterator for standard containers and const T* for arrays. Those functions are called std::cbegin, std::cend etc.

Adopting third party containers

Suppose you have a third party library that defines some containers that instead of C++ style iterators provides some other means of iteration. Maybe they provide a Java style iterator class with hasNext, next and get functions or they only provide an indexing operator or something different.

In the past, loops over such containers had to be written different from loops over standard library containers. Even though it is usually possible to write a custom iterator class that provides operator++, operator* and anything you need for iterators, it is not possible to add begin and end member functions to such a container class that would return the corresponding custom iterators.

std::begin and std::end as free functions can be specialized for any such container to return the custom iterators, making it possible to use the standard form of full iteration loops, making the code more uniform, which reduces unnecessary attention to implementation details.

Prefer to use begin(c) and end(c) over c.begin() and c.end(), since they are more likely to continue to do the right thing if the type of c changes.

Which brings us to the second feature that beautifully rounds up the full iteration loops:

Range based for loops

Since C++11 there is another style of for loops, which looks like this:

Which is roughly correspondent to

Note that this is quite a simplification of the actual wording in the C++ standard, but the spirit remains: It is a full range loop from begin() to end() of the range denoted by range_expr. Both begin and end are looked up via argument dependent lookup with the speciality that namespace std is always considered, too.

That means, that if you provide anything as range_expr where calling free functions begin and end makes any sense, either because you provided the functions in the associated namespace or because you overloaded std::begin and std::end, this loop construct will just work.

So, the initial examples of full range loops could be rewritten like this:

Note the auto&& for the loop element variable – this kind of declaration always works, independent of if you pass a const or non-const range, and regardless of what kind of value (r-value or l-value) the dereferencing of iterators will return. In fact, there has been a proposal to enable the omission of a type specifier for num making it auto&& by default.

Prefer using auto&& as type specifier for the loop element variable.

There are some quirks to the lookup rules:

  • If the range expression is a plain array, the will no functions be called, and range based for will just use the pointer to the array and one past the end.
  • If the range expression is of a type that has begin and end member functions, those will be called instead of any free functions.

However, those special rules only matter in corner cases, or if you do something fancy and unexpected, which is usually not a good idea. So, for completeness a few guidelines for overloading begin and end functions:

  • Don’t overload or specialize any begin and end functions for plain arrays.
  • If you write your own container classes, provide the right begin and end member functions as well as free functions and std::begin and std::end specializations (if possible).
  • Be consistent: std::begin and std::end specializations, free begin and end functions and begin and end member functions should do exactly the same to avoid confusion and surprises – implement one in terms of the others.

If you stick to these rules and to sensible and readable code in general, range based for will as good as always work (I know of no exceptions, but with C++ and its different compilers you never know for sure). So, since range based for looks different to other for loops, it very clearly says “This is a full range loop – no exceptions”.

Prefer range based for over classic iterator loops from begin to end.

Facebooktwittergoogle_plusredditlinkedinFacebooktwittergoogle_plusredditlinkedinby feather

Leave a Reply

Your email address will not be published. Required fields are marked *