Contact
Evil Genius SocietyHomeLearnForumLeaderboardSocialsMessages
← Back to module

Lesson 4 of 6

Most loops exist to visit every element of something. C++11 added a form that says exactly that, with no counter to get wrong:

#include <iostream>

int main() {
    int scores[] = {90, 85, 77, 100};

    for (int score : scores) {
        std::cout << score << " ";
    }
    std::cout << "\n";
    return 0;
}
90 85 77 100 

Read the : as "in": for each score in scores.

Compare it to the indexed version:

for (int i = 0; i < 4; ++i) {
    std::cout << scores[i] << " ";
}

The range-based version has no index, no bound to get wrong, and no way to run off the end. When you don't actually need the position, it's strictly better.

It works on anything with a beginning and an end — C arrays, std::vector, std::string, std::map, and initializer lists:

#include <iostream>
#include <string>

int main() {
    for (int n : {2, 4, 6}) {
        std::cout << n << " ";
    }
    std::cout << "\n";

    std::string word = "Evil";
    for (char c : word) {
        std::cout << c << ".";
    }
    std::cout << "\n";
    return 0;
}
2 4 6 
E.v.i.l.

The copy problem

By default, the loop variable is a copy of each element. For int that's free. For anything larger it isn't — and, more importantly, modifying the copy doesn't change the original:

#include <iostream>

int main() {
    int values[] = {1, 2, 3};

    for (int v : values) {
        v = v * 10;              // modifies the copy only
    }

    for (int v : values) {
        std::cout << v << " ";
    }
    std::cout << "\n";
    return 0;
}
1 2 3 

Nothing changed. The loop faithfully doubled three temporary copies and threw them away.

& to modify, const& to read

Add & and the loop variable becomes a reference — another name for the actual element:

#include <iostream>

int main() {
    int values[] = {1, 2, 3};

    for (int& v : values) {
        v = v * 10;
    }

    for (int v : values) {
        std::cout << v << " ";
    }
    std::cout << "\n";
    return 0;
}
10 20 30 

That one character is the whole difference. References get their own treatment in Module 7; for now, the practical rule:

| Form | Use when | | --- | --- | | for (T x : c) | elements are cheap to copy (int, char, double) and you only read | | for (T& x : c) | you want to modify the elements | | for (const T& x : c) | you only read, but copying would be expensive |

const T& is the one to reach for by default with strings and objects — it avoids the copy and documents that you're not changing anything:

#include <iostream>
#include <string>

int main() {
    std::string words[] = {"evil", "genius", "society"};

    for (const std::string& w : words) {
        std::cout << w << "(" << w.size() << ") ";
    }
    std::cout << "\n";
    return 0;
}
evil(4) genius(6) society(7) 

Copying three strings to print them would be pure waste. With const std::string&, nothing is copied and the compiler enforces that you don't modify them.

const auto& (lesson 3.6) is the common shorthand when the type is long.

When you still need an index

Range-based for gives you elements, not positions. If you need to know where you are, or to compare neighbours, use an indexed loop:

#include <iostream>

int main() {
    int values[] = {5, 3, 9, 1};

    for (int i = 0; i < 4; ++i) {
        std::cout << i << ":" << values[i] << " ";
    }
    std::cout << "\n";
    return 0;
}
0:5 1:3 2:9 3:1 

The rule

Use range-based for unless you specifically need the index. It's shorter, it can't run off the end, and it states the intent — visit everything — instead of encoding it in arithmetic.

Try it

Given double prices[] = {10.0, 25.5, 4.75};, use range-based for twice: once to apply a 10% discount to every price in place, and once to print them.

Checkpoint

  • for (T x : container) visits every element with no index to get wrong.
  • The loop variable is a copy by default, so modifying it does nothing to the container.
  • T& makes it a reference so you can modify elements in place.
  • const T& avoids copying without allowing modification — the default for strings and objects.
  • Fall back to an indexed loop only when you genuinely need the position.

Questions about this lesson

Log in to ask a question.