Video Coming Soon...
18: Range for-loops
There's one more way to iterate (loop through) items called a range-for
. The range-for
is easier to use for 90% of your looping tasks, but it does have a small problem which I'll discuss later. I recommend using the range-for
to do most of your list processing, followed by classic for-loop
for the rest, and only use while-loop
in very raare situations.
The Problem with for-loops
The problem with the classic for-loop
is it's only a formal way to write a while-loop
. You are still manually writing an "initializer", "test", and "incrementer" but it's in one place instead of spread around like in a while-loop
. Still, you can write your for-loops
wrong or accidentally misuse it in some situations. For example, if you do this:
vector<string> fruit = {
"Apple", "Orange", "Pear",
"Grape", "Durian", "Mango"
};
for(int i = 0; i < fruit.size(); i++) {
println("EVEN FRUIT {}", fruit[i]);
i += 1; // oops, problems?
println("ODD FRUIT {}", fruit[i]);
}
This code isn't something you'll do on purpose, but you may accidentally alter the variable without realizing it. Still, it is reasonablly difficult to abuse a for-loop
compared to a while-loop
so they aren't wrong to use, and many times they're the right thing to use (see below).
The range-for
Alternative
The range-for
takes advantage of the fact that most C++ containers (vector
, array
, etc.) have information that describes their contents. Since a vector
knows how many elements it contains, the vector
can control the loop for you. The range-for
leverages this information to do the loop automatically for you:
vector<string> fruit = {
"Apple", "Orange", "Pear",
"Grape", "Durian", "Mango"
};
for(auto name : fruit) {
println("FRUIT {}", name);
}
This will loop the same as the classic for-loop
but you don't have to manually track a counter, test it's within range, or even get the variable for the contents. I read for(auto name : fruit)
as "for name of fruit" but you could also say "for name in fruit".
The Problem with range-for
There are two problems with range-for
to be aware of:
- There is an official bug in
range-for
related to "temporary" variables. It's a bit too out of scope for this point in the course, but you can see a possible example of it in the code below. The way to avoid the bug for now is to only loop over contents of a variable, not the results of calculations. We'll cover it more later. - It's difficult to get the current index in a
range-for
. You can do it, but it's just easier to use the classicfor-loop
if you also need the index.
As an example of #2, if you want to print what number each fruit is at you'd do this:
for(int i = 0; i < fruit.size(); i++) {
println("Fruit #{} is {}", i+1, fruit[i]);
}
The Code
The code for this exercise is large, so take it slow:
View Source file ex18.cpp Only
#include <fmt/core.h>
#include <vector>
using std::string, std::vector;
using namespace fmt;
/* This is a function, just get this working for now
* and we'll cover them soon.
*/
vector<string> bug_maker() {
return {"Boo! I'm a Bug!"};
}
int main() {
vector<string> fruit = {
"Apple", "Orange", "Pear",
"Grape", "Durian", "Mango"
};
// loop through fruit
for(auto name : fruit) {
println("Fruit is {}", name);
}
// set them all to something else
vector<string> guitars = {
"Stratocaster", "Telecaster", "Bass VI",
"P-Bass", "ASAT Special", "G&L S500"
};
// loop through guitars
for(auto name : guitars) {
println("Guitar is {}", name);
}
/*
for(auto name : bug_maker()[0]) {
println("Probably Crashing: {}", name);
}
*/
}
Get this working then we'll study the code.
Study Time
It's time to learn to do your own code walk throughs. You actually know everything in this code, but you need to confirm you know it. Your task for this section is to document each line of the code and explain what it does. As you do this keep track of what you really know, and what you're unclear about. All of your explanations should be simple, so if you find yourself trying to bullshit your way through a description then stop, and go read the documentation and then exercises to refresh your memory.
The best way to document each line is to write a little comment above the line like this:
// print hello to the screen
println("Hello");
NOTE: Sometimes I'll say "to the screen" but I almost always mean into your Terminal window. I don't mean randomly to somewhere on your physical screen. I mention this because some people do think that at first.
Breaking It
In the code for the exercise there's a commented out part that is supposed to possibly cause an error if you run it. I couldn't get it to fail, but maybe you can. Or, maybe it's not quite the cause of the error. I leave this as an advanced topic because to really understand as it's unreliable to trigger.
In general you should avoid this pattern in range-for
by only using range-for
on actual variables you've assigned, and not on immediate return values from functions.
Challenge Mode: std::find
For an advanced challenge I have this code example ex18b.cpp
I want you to study and try to figure out:
View Source file ex18b.cpp Only
#include <fmt/core.h>
#include <vector>
using std::string, std::vector;
using namespace fmt;
int main() {
vector<string> fruit = {
"Apple", "Orange", "Pear",
"Grape", "Durian", "Mango"
};
std::string target = "Orange";
auto found = std::find(fruit.begin(), fruit.end(), target);
if(found == std::end(fruit)) {
println("DIDN'T FIND IT");
} else {
println("FOUND: {}", *found);
}
}
I'm not going to tell you what this does. Your job is to research everything in there, find what you don't know, then look it up on cppreference.com on your own. Doing your own research to learn code you don't know is a key part of being a programmer, and I think it's about 30% of what I do all day.
Further Study
- You should now combine what you know about functions from Exercise 16 with what you know about
for-loop
andrange-for
. There's many ways to do this, but try calling a function in the loop, or calling a function that does the loop. - Take the first code example and convert every
range-for
to a classicfor-loop
. - Once it's working, convert the code back manually to use
range-for
. If you do these two you should have a firm grasp of both.
Register for Learn C++ the Hard Way
Register to gain access to additional videos which demonstrate each exercise. Videos are priced to cover the cost of hosting.