Skip to Content
Course content

14: Range-Based For Loops

Click on the "Edit" button in the top corner of the screen to edit your slide content.

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 like int or char where 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 (like std::string or custom structs) 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:

  1. Create a struct Product containing a std::string name and a double price.
  2. Initialize a std::vector<Product> with at least four items (e.g., "Laptop", "Desk Chair", "USB Cable", "Monitor").
  3. Use a range-based for loop to iterate through the inventory.
  4. 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%.
  5. Use a second range-based for loop (using a const reference) 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!

Rating
0 0

There are no comments for now.

to be the first to leave a comment.