C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
14: Range-Based For Loops
I want to show you a snippet of code that looks perfectly fine at first glance. I've seen junior devs (and even some seniors) spend an hour scratching their heads over this exact pattern. Imagine we're writing a simple grading system where we want to give every student a 5-point curve.
#include <iostream>
#include <vector>
#include <string>
struct Student {
std::string name;
int score;
};
int main() {
std::vector<Student> classroom = {
{"Alice", 82},
{"Bob", 75},
{"Charlie", 90}
};
for (auto s : classroom) {
s.score += 5;
}
for (auto s : classroom) {
std::cout < s.name < ": " < s.score < "\n";
}
return 0;
}
If you run this, you'll notice something frustrating: the scores haven't changed. Alice is still at 82, Bob is still at 75. The loop ran, the addition happened, but the data in the vector remained untouched. Why?
The Hidden Copy Trap
The problem lies in auto s. In a range-based for loop, the variable you declare is initialized with the current element of the collection. By writing auto s, you're telling C++ to create a copy of each Student object in the vector for every iteration.
Inside the loop, you are indeed adding 5 to s.score, but s is just a temporary local variable that lives for one iteration and then vanishes. You're essentially editing a photocopy of a document and then throwing the photocopy in the trash, wondering why the original file didn't change.
Fixing it with References
To fix this, we need to tell C++ that we want to work with the actual object sitting inside the vector, not a duplicate. We do this by using a reference: auto& s.
// Change this:
for (auto s : classroom)
// To this:
for (auto& s : classroom) {
s.score += 5;
}
Now, s becomes an alias for the element currently being visited. When you modify s.score, you're modifying the memory directly inside the classroom vector. This is not only correct for mutation, but it's also significantly faster because you aren't copying strings and integers every time the loop ticks.
Choosing the Right Type for the Job
You might be thinking, "Should I just use auto& for everything then?" Not quite. If you're only reading data and not changing it, using a plain reference is okay, but a const reference is the gold standard. It tells the compiler (and other engineers reading your code) that this loop is "read-only."
for (auto x : container): Use this for tiny types likeintorcharwhere copying is cheap and you don't need to change the original.for (auto& x : container): Use this when you need to modify the elements in the container.for (const auto& x : container): Use this for expensive objects (likestd::stringor customstructs) when you only need to read the data.
I generally default to const auto& for anything larger than a primitive. It's a safe habit that prevents accidental mutations and keeps your program lean.
Beyond Vectors
The beauty of the range-based for loop is that it doesn't care if you're using a std::vector, a std::list, a std::map, or even a raw C-style array. As long as the container has begin() and end() methods (or is a fixed-size array), the syntax remains identical. It cleans up the clutter of iterators and index counting, letting you focus on the actual logic of your loop.
📋 Practical Task
The Warehouse Inventory Auditor
You are building a tool for a warehouse manager. You have a list of Product objects, each with a name and a current price. Some products have gone out of style and need a 20% discount, while others need a 10% price increase due to inflation.
Your Task:
- Create a
struct Productcontaining astd::string nameand adouble price. - Initialize a
std::vector<Product>with at least four items (e.g., "Laptop", "Desk Chair", "USB Cable", "Monitor"). - Use a range-based for loop to iterate through the inventory.
- Inside the loop, apply the following logic:
- If the product name is "USB Cable", decrease its price by 20%.
- If the product name is "Laptop", increase its price by 10%.
- Use a second range-based for loop (using a
constreference) to print the final name and price of every product in the warehouse.
Make sure you choose the correct reference type for both loops to ensure the price updates actually persist and the printing loop is optimized!
There are no comments for now.