Sometimes you don’t want to count; you want to act on each element of a known set. The range-based for loop expresses exactly that:
for (/* element */ : /* range */) {
// do something with element
}
It reads naturally: “for each element in this range.” This is great for finite sums/products, evaluating a function at a handful of sample points, or scanning a short list of special cases.
We’ll later use range-based loops with arrays and standard containers (vectors, maps, …). For now, our examples use only initializer lists like
{1, 3, 5}so we don’t rely on topics we haven’t covered yet.
Syntax in one glance
for (int k : {1, 3, 5, 7, 9}) {
// k takes values 1, 3, 5, 7, 9 (in that order)
}
- The part before the colon declares a new variable that receives each element in turn.
- The part after the colon is any range expression. Here it’s an initializer list
{ ... }. - Use braces
{}for the loop body—even for one line—for clarity.
Example 1 — A finite sum over a selected set
Compute \(\sum_{k \in {1,3,5,7,9}} k^2\):
#include <iostream>
int main() {
int sum = 0;
for (int k : {1, 3, 5, 7, 9}) {
sum = sum + k * k;
}
std::cout << "Sum of odd squares = " << sum << '\n';
return 0;
}
This pattern matches math notation directly: pick exactly the indices you care about and add them up.
Example 2 — Horner’s method via coefficients
Evaluate \(P(x) = 2x^3 – 3x^2 + 0x + 1\) using Horner’s method (coefficients from highest to lowest):
#include <iostream>
int main() {
double x;
std::cout << "Evaluate P(x)=2x^3-3x^2+0x+1 at x = ";
std::cin >> x;
double y = 0.0;
for (double c : {2.0, -3.0, 0.0, 1.0}) {
y = y * x + c; // Horner step
}
std::cout << "P(" << x << ") = " << y << '\n';
return 0;
}
Here the range is the finite list of coefficients.
Example 3 — Early exit with break: first small divisor test
Scan a small set of primes and stop at the first hit:
#include <iostream>
int main() {
int n;
std::cout << "Enter n (>= 2): ";
std::cin >> n;
int first_divisor = -1;
for (int d : {2, 3, 5, 7, 11, 13, 17, 19}) {
if (n % d == 0) {
first_divisor = d;
break; // we’re done scanning the set
}
}
if (first_divisor == -1) {
std::cout << "No small prime divisor found\n";
} else {
std::cout << "First small prime divisor: " << first_divisor << '\n';
}
return 0;
}
break exits the loop immediately (ties back to the previous post).
Example 4 — Skip cases with continue: sum only positives
#include <iostream>
int main() {
int sum = 0;
for (int v : {3, -1, 4, 0, -2, 5}) {
if (v <= 0) {
continue; // ignore non-positive values
}
sum = sum + v;
}
std::cout << "Sum of positives = " << sum << '\n';
return 0;
}
This matches a common mathematical “filter”: include only values that satisfy a property.
Example 5 — Sampling a function at chosen points
Evaluate \(f(x)=x^2\) at a few handpicked points:
#include <iostream>
int main() {
double total = 0.0;
for (double x : {0.0, 0.25, 0.5, 0.75, 1.0}) {
total = total + x * x;
}
std::cout << "Sum of samples of x^2 = " << total << '\n';
return 0;
}
Range-based loops shine whenever you want to name the points explicitly.
Notes and gotchas (for now)
- The loop variable receives a copy of each element here; that’s fine for numbers.
- With initializer lists, elements are not modifiable (there’s nothing to mutate). We’ll see how to iterate by reference once we introduce arrays and containers.
- Range-based
fordoesn’t give you an index automatically. If you need one, keep a counter:
int i = 0;
for (int k : {2, 4, 6, 8}) {
// use i as the position (0,1,2,3)
i = i + 1;
}
- If you simply need to repeat a fixed small number of times, a classic
foris clearer. A range like{0,0,0,0,0}also works—but it’s a teaching trick, not a habit.
Mini-reference
- Form:
for (Type name : {v1, v2, v3}) { ... } - Works today with initializer lists; later with arrays and containers.
breakends the loop;continueskips to the next element.
What’s next
We’ll revisit range-based loops when we cover arrays and standard containers—that’s where they really shine. For now, keep them in mind whenever your problem is naturally “for each chosen value, do X.”
