Introduction

I have bought another book, A Tour of C++, 2nd Edition, by Bjarne Stroustrup. Stroustrup is the original creator of C++ and the author of many books on it that have been extremely well received.

Published in July of 2018, he does a 256 page whirlwind tour of C++, concentrating on C++17 (the current standard), and including some forward looking material for C++20 (draft). Much is backward compatible with the C++11 and C++14 standards and before. It is part of the The C++ In-Depth Series, whose authors were chosen by Stroustrup.

Range Based for Loop

This first post explores the first thing he discussed that really caught my attention about C++—the range based for loop (range-for-statement). When it can be used appropriately for simple needs, it is much more succinct and less error prone than the original ANSI C style for statement. It was added to the standard C++11 back in 2011.

Standard for Statement

The only option in ANSI C, or C++ before C++11, was the standard for statement:

int intAry[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int numElements = sizeof(intAry) / sizeof(int);

for (int ii = 0; ii < numElements; ii++) {
  std::cout << intAry[ii] << ' ';
}
// prints out: 0 1 2 3 4 5 6 7 8 9

Notice the programming steps we must take above. First, we simultaneously declare, set the number of elements of, and initialize the integer array that holds the values we want to print. Then, for the standard for statement, we must do all the following steps:

  • Get the size of the entire array using sizeof().
  • Get the size of each array element using sizeof().
  • Calculate the number of elements by dividing the size of the array by the size of each element.
  • Declare an index variable.
  • Initialize the index variable to zero to point to the first element in the zero index based array.
  • Include a conditional expression to stop the looping when the index value reaches the correct upper limit using the previously calculated number of elements.
  • Explicitly define the correct index increment statement.
  • Use the index variable inside the [] operator on the array variable each iteration to get the appropriate value.

That is a lot we must do to print out a simple list of integer values. Even though it is compact, the (relatively) error-prone programmer must do a lot of steps for such a simple thing. Forgot to initialize the index variable? Used commas instead of semicolons in the for statement? Calculated the number of elements wrong? Used the wrong comparison operator in the conditional expression? See generalized diagram below:

`for` loop

What the code in the for loop actually accomplishes even sounds complex when written out: “Initialize the index variable ii to zero. For each iteration, check if the index variable is still less than the number of elements. If not, we’re done. If so, print out the appropriate value of the array element for the current loop iteration pointed to by the index variable, then increment the index variable and repeat.”

Range to the Rescue!

Compare the above code and steps with the code and steps below which accomplishes the same thing using the range-for-statement:

int intAry[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

for (auto xx : intAry) {
  std::cout << xx << ' ';
}
// prints out: 0 1 2 3 4 5 6 7 8 9

As above, we simultaneously declare and initialize the array, then in the range-for-statement we:

  • Declare an appropriate variable left of the colon (the range_declaration) to receive the desired value each iteration.
  • Include the range_expression to the right of the colon to define the variable from which the values will be read.
  • Directly use the variable declared to the left of the colon in the body of the range-for-statement in our print statement.

The number of steps the programmer must do himself is reduced from 8 to 3, and all the low-level, error prone complexity is now being handled automatically by the compiler, presumably error free.

What this code actually does is the following (quoting from Stroustrup, with appropriate changes): “For every element of intAry, from the first to the last, place a copy in xx and print it.” See generalized diagram below:

Range *for* Loop

The compiler can automatically determine how to read and write sequentially from the type signatures of the range_declaration and range_expression. With the the auto type used in the range_declaration to declare the receptor variable xx, the compiler takes care of picking the appropriate type for it based on the range_expression type.

It Gets Even Better

Stroustrup shows how you can shrink this code down even further by including the list of array values as an expression directly in the range-for-statement:

for (auto xx : { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }) {
  std::cout << xx << ' ';
}
// prints out: 0 1 2 3 4 5 6 7 8 9

We are now down to just two source lines of code to do this task. The range_expression is now the same as the initializer expression used in the previous code to set the number of elements in the array and initialize their values.

And Even Better

So far, we have just done a very simple thing—print out a sequence of unchanging integer values from an array. Stroustrup shows how to work a little magic with reference types to increment each value in the array, all very succinctly and with few chances for error:

int intAry[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

for (auto& xx : intAry) {
  ++xx;
}

for (auto xx : intAry) {
  std::cout << xx << ' ';
}
// prints out: 1 2 3 4 5 6 7 8 9 10

In the first range-for-statement above, we declare a reference variable using the reference unary suffix operator &. Instead of copying the value of each successive integer in the array into xx, when a reference variable is declared, the compiler points the reference variable to the location of each successive integer in the array.

In the statement body, when we increment the reference variable, it actually increments the integer value in the original location that the reference variable points to, which is an element in the original array intAry. Here is a diagram that shows this graphically:

Reference to Integer Array Increment

The second range-for-statement just prints out the new values in the array to show that they were indeed incremented (going now from 1 to 10 instead of from 0 to 9).

You can see the complete highlighted code for the examples above in my online Repo for this project. Run it and edit it on Coliru.

Conclusion

The range-for-statement is an easy to use, welcome addition to C++. Not only does it make the programming less protracted and error prone, it is easier to read and understand.

This series on A Tour of C++ should be a very good one, as the book is excellent and up-to-date, showing many features of C++17 and C++20 to explore and learn. Stay tuned for more as I get excited about new features in C++, or find new and better ways to use old ones, and explore them here with you.