In lesson 6.3 -- Arrays and loops, we showed examples where we used a for loop to iterate through each element of an array.
For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <iostream> #include <iterator> // std::size int main() { constexpr int scores[]{ 84, 92, 76, 81, 56 }; constexpr int numStudents{ static_cast<int>(std::size(scores)) }; int maxScore{ 0 }; // keep track of our largest score for (int student{ 0 }; student < numStudents; ++student) { if (scores[student] > maxScore) { maxScore = scores[student]; } } std::cout << "The best score was " << maxScore << '\n'; return 0; } |
While for loops provide a convenient and flexible way to iterate through an array, they are also easy to mess up and prone to off-by-one errors.
There’s a simpler and safer type of loop called a for-each loop (also called a range-based for-loop) for cases where we want to iterate through every element in an array (or other list-type structure).
For-each loops
The for-each statement has a syntax that looks like this:
for (element_declaration : array) statement;
When this statement is encountered, the loop will iterate through each element in array, assigning the value of the current array element to the variable declared in element_declaration. For best results, element_declaration should have the same type as the array elements, otherwise type conversion will occur.
Let’s take a look at a simple example that uses a for-each loop to print all of the elements in an array named fibonacci:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> int main() { constexpr int fibonacci[]{ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 }; for (int number : fibonacci) // iterate over array fibonacci { std::cout << number << ' '; // we access the array element for this iteration through variable number } std::cout << '\n'; return 0; } |
This prints:
0 1 1 2 3 5 8 13 21 34 55 89
Let’s take a closer look at how this works. First, the for loop executes, and variable number is set to the value of the first element, which has value 0. The program executes the statement, which prints 0. Then the for loop executes again, and number is set to the value of the second element, which has value 1. The statement executes again, which prints 1. The for loop continues to iterate through each of the numbers in turn, executing the statement for each one, until there are no elements left in the array to iterate over. At that point, the loop terminates, and the program continues execution (returning 0 to the operating system).
Note that variable number is not an array index. It’s assigned the value of the array element for the current loop iteration.
For each loops and the auto keyword
Because element_declaration should have the same type as the array elements, this is an ideal case in which to use the auto
keyword, and let C++ deduce the type of the array elements for us.
Here’s the above example, using auto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> int main() { constexpr int fibonacci[]{ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 }; for (auto number : fibonacci) // type is auto, so number has its type deduced from the fibonacci array { std::cout << number << ' '; } std::cout << '\n'; return 0; } |
For-each loops and references
In the following for-each example, our element declarations are declared by value:
1 2 3 4 5 |
std::string array[]{ "peter", "likes", "frozen", "yogurt" }; for (auto element : array) // element will be a copy of the current array element { std::cout << element << ' '; } |
This means each array element iterated over will be copied into variable element. Copying array elements can be expensive, and most of the time we really just want to refer to the original element. Fortunately, we can use references for this:
1 2 3 4 5 |
std::string array[]{ "peter", "likes", "frozen", "yogurt" }; for (auto& element: array) // The ampersand makes element a reference to the actual array element, preventing a copy from being made { std::cout << element << ' '; } |
In the above example, element will be a reference to the currently iterated array element, avoiding having to make a copy. Also any changes to element will affect the array being iterated over, something not possible if element is a normal variable.
And, of course, it’s a good idea to make your reference const
if you’re intending to use it in a read-only fashion:
1 2 3 4 5 |
std::string array[]{ "peter", "likes", "frozen", "yogurt" }; for (const auto& element: array) // element is a const reference to the currently iterated array element { std::cout << element << ' '; } |
Rule
In for-each loops element declarations, if your elements are non-fundamental types, use references or const
references for performance reasons.
Rewriting the max scores example using a for-each loop
Here’s the example at the top of the lesson rewritten using a for each loop:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <iostream> int main() { constexpr int scores[]{ 84, 92, 76, 81, 56 }; int maxScore{ 0 }; // keep track of our largest score for (auto score : scores) // iterate over array scores, assigning each value in turn to variable score { if (score > maxScore) { maxScore = score; } } std::cout << "The best score was " << maxScore << '\n'; return 0; } |
Note that in this example, we no longer have to manually subscript the array or get its size. We can access the array element directly through variable score. The array has to have size information. An array that decayed to a pointer cannot be used in a for-each loop.
For-each loops and non-arrays
For-each loops don’t only work with fixed arrays, they work with many kinds of list-like structures, such as vectors (e.g. std::vector
), linked lists, trees, and maps. We haven’t covered any of these yet, so don’t worry if you don’t know what these are. Just remember that for each loops provide a flexible and generic way to iterate through more than just arrays.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <iostream> #include <vector> int main() { std::vector fibonacci{ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 }; // note use of std::vector here rather than a fixed array // Before C++17 // std::vector<int> fibonacci{ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 }; for (auto number : fibonacci) { std::cout << number << ' '; } std::cout << '\n'; return 0; } |
For-each doesn’t work with pointers to an array
In order to iterate through the array, for-each needs to know how big the array is, which means knowing the array size. Because arrays that have decayed into a pointer do not know their size, for-each loops will not work with them!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <iostream> int sumArray(const int array[]) // array is a pointer { int sum{ 0 }; for (auto number : array) // compile error, the size of array isn't known { sum += number; } return sum; } int main() { constexpr int array[]{ 9, 7, 5, 3, 1 }; std::cout << sumArray(array) << '\n'; // array decays into a pointer here return 0; } |
Similarly, dynamic arrays won’t work with for-each loops for the same reason.
Can I get the index of the current element?
For-each loops do not provide a direct way to get the array index of the current element. This is because many of the structures that for-each loops can be used with (such as linked lists) are not directly indexable!
Since C++20, range-based for-loops can be used with an init-statement just like the init-statement in if-statements. We can use the init-statement to create a manual index counter without polluting the function in which the for-loop is placed.
The init-statement is placed right before the loop variable:
for (init-statement; element_declaration : array) statement;
In the following code, we have two arrays which are correlated by index. For example, the student with the name at names[3]
has a score of scores[3]
. Whenever a student with a new high score is found, we print their name and difference in points to the previous high score.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> int main() { std::string names[]{ "Alex", "Betty", "Caroline", "Dave", "Emily" }; // Names of the students constexpr int scores[]{ 84, 92, 76, 81, 56 }; int maxScore{ 0 }; for (int i{ 0 }; auto score : scores) // i is the index of the current element { if (score > maxScore) { std::cout << names[i] << " beat the previous best score of " << maxScore << " by " << (score - maxScore) << " points!\n"; maxScore = score; } ++i; } std::cout << "The best score was " << maxScore << '\n'; return 0; } |
Output
Alex beat the previous best score of 0 by 84 points! Betty beat the previous best score of 84 by 8 points! The best score was 92
The int i{ 0 };
is the init-statement, it only gets executed once when the loop starts. At the end of each iteration, we increment i
, similar to a normal for-loop. However, if we were to use continue
inside the loop, the ++i
would get skipped, leading to unexpected results. If you use continue
, you need to make sure that i
gets incremented before the continue
is encountered.
Before C++20, the index variable i
had to be declared outside of the loop, which could lead to name conflicts when we wanted to define another variable named i
later in the function.
Conclusion
For-each loops provide a superior syntax for iterating through an array when we need to access all of the array elements in forwards sequential order. It should be preferred over the standard for loop in the cases where it can be used. To prevent making copies of each element, the element declaration should ideally be a reference.
Quiz time
This one should be easy.
Question #1
Declare a fixed array with the following names: Alex, Betty, Caroline, Dave, Emily, Fred, Greg, and Holly. Ask the user to enter a name. Use a for each loop to see if the name the user entered is in the array.
Sample output:
Enter a name: Betty Betty was found.
Enter a name: Megatron Megatron was not found.
Hint: Use std::string_view
as your array type.
![]() |
![]() |
![]() |
So the size of the array must be known at compile time to work?
And the size of array isn't known because array decayed to pointer pointing to a single element (4bytes) so it's not representing the actual size of array?
But it doesn't matter because it decays to pointer anyway, so how is passing an array to a function then using that array in a for each loop any different than using that array even without it being passed?
My code
> it decays to pointer anyway
It only decays when you cast it, ie. by passing it to a function that expects a pointer. `names` is an array when you use it in line 9.
Ok thank you.
Hi there,
As the for-each function won't work with array decaying, could we pass a reference of the array as the function parameter then? My code is like this:
Any problems (improvement) with that?
Thanks,
Eric
- Limit your lines to 80 characters in length for better readability.
- If your program prints anything, the last thing it prints should be a line feed.
You can pass the array by reference, but then the function will only work with arrays of that size. If your function doesn't need to work with other sizes, that's fine.
Is it possible to use vector::erase to remove elements using a for-each loop?
No. Don't modify the list you're iterating over. You can modify the elements, but don't modify the list.
For simplicity, let's say @begin is 0 and @end is 4.
@it is currently 3 and you erase 2 elements. now @end is 2.
since 3 > 2, this loop is infinite.
Here is my solution.
- If your program prints anything, the last thing it prints should be a line feed ('\n').
- `main`: Missing return-statement. Should be added for consistency.
I am trying to do the quiz and I have finished it. I have now decided to try and make it so it supports names
like
John Doe.
I am just curious.
Yes, I did not use all the names you wanted me to. But Why is it that this works but if I do
Instead of
It does not work and I have to press enter twice after choosing a name?
Thanks!
`std::getline` extracts everything until it finds a '\n'. It removes the '\n'.
`std::cin.operator>>` extracts everything until it finds something that can't be stored in the type (eg. a '\n'). It doesn't remove the last character.
Using `std::cin.operator>>` leaves a '\n' in the input stream. `std::getline` sees this '\n' and stops extraction. `std::cin.ignore` can be used to remove this trailing '\n' to allow `std::getline` to work properly.
Thanks! I think I understand better now.
Does a for-each loop work with 2d arrays?
I was going to add a way of checking whether the name had been already checked, like a guest list.
So I thought I'd try an array of struct variables something like this
and the array of guests
...and then I got stuck with the loop syntax. Can you do this with a for-each loop?
Any feedback greatly appreciated
Your array is wrong. The data is 1-dimensional.
I've changed it to a 4x2 array and added a loop.
I see where I was going wrong with the array at least. I didn't even need a 2 dimensional array for that struct! I'll have to dissect the loop in your post a bit more to understand it.
Thanks for the reply!
the code in your solution does not use curly brackets for the for-each loop.
but here we have more than one line of code inside the for-each loop.
Do I need to use them? how does the compiler knows where to stop looking "inside" the for loop?
Thank you
The for-loop, just like if and while, will execute the next statement, no matter how many lines it has.
For readability, if your statement exceeds 1 line, use curly braces.
Hello there,
I just found another kind of for loop called foreach, too.
So what kind of this and how does it work ?
I found it when I wanted to use for loop and Qt Creator completed it.
foreach is not a standard C++ feature, buy provided by qt. It should not compile without additional includes if you disabled compiler extensions.
Hello, what do you think about this kind of output formatting? Thank you for your insights :)
* Line 9: You don't need to specify the length if you initialize the array.
* The loop should break once the name was found.
Using conditional operators in strings is fine, unless you plan on localizing (translating) your strings.
You could remove more of the strings, as the only difference is "not".
great, thanks!
Dear teachers, why we should use const std::string in this example?
Because namesArray is used in a read-only fashion.
How do I know if my program is running faster one way compared to another? Is there a way to check? Thanks.
Lesson 8.16 Timing your code
Hey just a question, is the break statement a reliable thing to use. I'm just wondering because I thought we shouldn't interrupt our control flow in our program because it's prone to errors.
Oh and any feedback on my code would be great, thanks :)
* Line 7, 8: Initialize your variables with brace initializers.
* Line 8: Limit your lines to 80 characters in length for better readability on small displays.
* Line 8: You don't need to specify the array size when you fully initialize an array.
* Inconsistent naming.
* Inconsistent formatting.
* Your loop only checks the first element, then breaks. Format your code, use curly brackets.
break can make code harder to understand, a condition in the loop's header is preferred.
This isn't always possible (eg. with for-each loops). Try to use break at the very top or bottom of loops where it's easy to spot without having to read the entire body of the loop.
Hi Alex!
The link to the "Arrays and Loops" lesson (At the very top) links to the http site.
Thanks. The redirection from http to https apparently broke when the site was restored. It's been fixed.
My CodeBlock IDE:
error: no match for 'operator==' (operand types are 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' and 'const int')
Could you explain this?
thank you!
There is no error in your code. Did you forget so save?
I saved.
I tried to copy and paste your code.
It gaved the same error.
It's OK when use flowing:
Make sure your compiler is C++11 compatible and C++11 support is enabled in the compiler settings.
thank you so much! Nascardriver
I got it.
I enable C++ 11 and it works
Can you help me understand the use for the Boolean variable "found" in the solution code? Boolean in general is giving me a tough time. Each time I have seen it used in a solution, it is initialized to false. Why is that?
* Line 6: Initialize your variables with brace initializers. You used copy initialization.
* Line 10: Initialize your variables with brace initializers.
* Line 7: Limit your lines to 80 characters in length for better readability on small displays.
Your code only compares the first name ("Alex") and ignores the rest.
@found is used, because printing the message from inside the loop makes the code harder to read. You need a way to get the result to the outside. You either find a name, or you don't, so you use a boolean. By default, you haven't found the name, so you initialize it to false.
Thank you Nascar! I see that the for each loop is just cylcing through the elements in the names array to see if the name is found or not. When found, found is true. Then name is printed as found or not, depending on whether found is true.
I'll play around with this myself, but if we had set found to true intially. Would we have written "return found;" in the body of our for loop, with no additional changes to the if(found) statement below? Thanks!
There should be no return-statement in the for-loop's body.
If @found was initialized to true, you should rename it to "notFound", update line 16 and negate line 20 (or swap line 21 and 23).
Hey! Is this okay? Could it be improved?
* Line 10, 13: Initialize your variables with brace initializers. You used copy initialization.
* Line 6: Initialize your variables with brace initializers.
* Line 11: Limit your lines to 80 characters in length for better readability on small displays.
* Use the auto-formatting feature of your editor.
* @namefound: Bad name, as it's the opposite.
Im using Visual Studio, do you know where i can find the auto formating feature?
Also:
Thanks for your quick response, really appreciate your engagement and effort you put into this site! :)
ctrl+k, ctrl+d
is there a reason the examples are using references for int despite the rule saying to do the exact opposite
It kept the examples significantly smaller.
Hi!
About this rule:
"Rule: In for-each loops element declarations, if your elements are non-fundamental types, use references or const references for performance reasons."
Does it mean that the scope of the declared variable in a for-each loop is only a single iteration of the loop and that the declaration statement is re-executed before each iteration?
As I understand, once a reference variable is initialized it cannot be forced to refer to something else.
@score gets re-declared in every iteration. You can think of it as
I don't really think that the thing with the namespace was a smart one.
* Don't use abbreviations unless their meaning is obvious (without context).
* It's easier to store @list_names in all lower/upper case. That way you don't have to have a special handling for the first letter. Also, @std::tolower, @std::toupper.
* @checkName could return directly once the name was found. That way you don't need @found. Also, @std::find.
* @isFound: Inconsistent naming
Son teaches father :)
Python teaches C++
it works when we change it as follows.
Hi,
decided to go a little further with the quiz on this one and make it continue until the user decided they had checked all the names they needed to check. It appears to be working as intended so just looking for any pointers/tips/bits i could do better:
thanks in advance!
* Line 30 cannot be true. Replace the do-while-loop with a while(true) loop
* @getTryAgain: The return value is unused. The reference to @tryAgain is overly complex. @getTryAgain should return a bool and not take any parameters.
* Line 36: Use uniform initialization (Remove the equals-sign)
* Line 39: @std::string's default constructor is faster than it's const char* constructor. Remove the quotation marks, leaving empty curly brackets.
* Line 52: Don't compare booleans to false/true
So if element_declaration in forEach loop is a reference, it is handled differently since normal references cant be reassigned(default const ptr) RIGHT?
Oh I figured it out. It is like a function parameter.
thanks nascardriver.
I'm looking forward to being able to actually measure the efficacy of my code, speed v mem used v ugliness? at the moment I have no way of comparing different versions.
> speed
Lesson 8.16
> mem used
Process manager.
With programs as small as the ones on learncpp you won't see much. This will get more important when you're writing big projects. In your previous code you allocated 4 bytes even though you might not need them. That's something you have to notice by looking at the code as you're not going to be able to see a big enough difference at run-time.
> ugliness
One person will think of it as pretty and other will hate it. Consistency is what you should be going for. Your code style might be weird, but make it the same weirdness all the way through.
Hi chaps, great site.
comments on my solution gratefully received.
Hi Jack!
Nice code. Setting @outString to " not" even if you don't need a string that long is a waste of resources, but that's nothing to worry about just yet.
I think the examples in this page are a bit misleading. I can see the benefit of using references instead of copy-by-value when looping over complex types like classes, but how can setting a reference to an int be faster than setting the int itself? A reference has the same (or greater) number of bytes than the fundamental type being references.
I suggest making this distinction in the tutorial.
Updated the summary rule to read:
Rule: In for-each loops element declarations, if your elements are non-fundamental types, use references or const references for performance reasons.
Thanks for the feedback!
My 2 cents,
Hi Kio!
Your code only works when checking for "Alex". None of the other names are found. Give it another try.
Don't pass 500 to @std::cin.ignore. Pass @std::numeric_limits<std::streamsize>::max() instead.
Hi Nas, you're right. The issue was my "break" line under else statement. I find Alex solution much better.
My solution
It will be great if you please explain how to read multi dimensional arrays using for each loop.
If already added in the later chapters then okay as I have yet to read the later topics.
Hi Dixit!
What do you mean by read?
I mean how to access.
Why can copying array elements be expensive?
Hi Baschti!
Every copy of memory is expensive. This isn't as noticeable when copying small amounts of data (eg. int), but there will be a performance impact when copying large types.