One of the more frequently used C++ constructs I use is the range-based for loop. The range-based forloop format was introduced in C++11, so you’ll need to enable that language set (or newer) to reap the benefits. This feature simplifies the verbosity of for loop constructs and is just plain nice to use.
The range-based for loop follows this general format:
for( declaration : expression)
{
//do some loop stuff
}
Here’s an simple example which prints all the elements in a std::vector:
std::vector<int> v1 = {-1, 3, 5, -8, 0};
std::cout << std::endl << "v1: " << std::endl;
for (const auto & t : v1)
{
std::cout << t << " ";
}
std::cout << std::endl;
The range expression typically corresponds to an array or a container object (requiring iterator support). You are also free to use initializer lists as an expression.
The declaration portion of the for loop involves declaring a named variable. The type of the variable needs to correspond to the type of the element in the expression (e.g. an int for an int array, an iterator for a container class). Remember the auto keyword? It’s very useful here to eliminate any thinking that may be required.
During each loop run, the variable you have declared is updated to reflect the next element in the sequence. This variable has to be declared, but it does not have to actually be used in the loop statement.
Note: the break and continue keywords still work as expected.
Types of Accesses
You can use a variety of declaration statements for accessing variables:
- Accessing by value (
auto t)- Makes a copy into a temporary object
- Accessing by reference (
auto &t)- Use this when you want to make changes to an element in the expression
- Accessing by
constreference (const auto &t)- Use if you want to prevent a copy of the object and don’t need to modify anything
- Especially useful if there is a copying penalty (e.g. large buffer to allocate, non-trivial construction).
- This is my default access format
Use the auto keyword!
Most examples you see involving range-based for loops will use the auto keyword. This is a matter of convenience and stops you from having to manually write out the full type name for your loop elements. From a simplicity and readability standpoint, it’s widely recommended that you use the auto keyword.
For instance, instead of:
for(std::map<int, set<std::string> >::iterator t : map_thing) { … }
You would say:
for(auto t : map_thing) { … }
Limitations
There are two minor limitations to note when using this for loop format:
- There is no index variable. If your loop operation requires an index, you are likely better off using the standard
forloop. - The loop iterates in a forward direction. If you want to iterate backward, you would need to use the standard
forloop format withrbegin()andrend()iterators.
Example Loops
I think the cppreference example is very clear and demonstrates the various types of accesses you can use:
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (const int& i : v) // access by const reference
std::cout << i << ' ';
std::cout << '\n';
for (auto i : v) // access by value, the type of i is int
std::cout << i << ' ';
std::cout << '\n';
for (auto&& i : v) // access by reference, the type of i is int&
std::cout << i << ' ';
std::cout << '\n';
for (int n : {0, 1, 2, 3, 4, 5}) // the initializer may be a braced-init-list
std::cout << n << ' ';
std::cout << '\n';
int a[] = {0, 1, 2, 3, 4, 5};
for (int n : a) // the initializer may be an array
std::cout << n << ' ';
std::cout << '\n';
for (int n : a)
std::cout << 1 << ' '; // the loop variable need not be used
std::cout << '\n';
}
Further Reading
Using C++ Without the Heap
Want to use C++, but worried about how much it relies on dynamic memory allocations? Our course provides a hands-on approach for learning a diverse set of patterns, tools, and techniques for writing C++ code that never uses the heap.
Learn More on the Course Page
