12.4 以範圍為基礎的 for 迴圈
「把容器裡的每個元素看一遍」大概是你寫過最多次的迴圈:
✘ 囉唆寫法:
for (int i = 0; i < (int)v.size(); i++) {
cout << v[i] << ' ';
}
C++11 起有更直白的寫法——以範圍為基礎的 for 迴圈(range-based for loop,多數人直接叫它英文名):
✔︎ 省力寫法:
for (int x : v) {
cout << x << ' ';
}
意思是:對 v 裡的每個元素 x,執行一次迴圈本體。不用索引、不用 size()、不用煩惱 10.4 的無號陷阱——範圍的頭尾編譯器自己管。陣列、vector、string 通通適用:
#include <bits/stdc++.h>
using namespace std;
int main() {
int a[3] = {2, 5, 9};
for (int x : a) cout << x << ' '; // 陣列
cout << '\n';
vector<int> v = {5, 3, 2, 7};
for (int x : v) cout << x << ' '; // vector
cout << '\n';
string s = "good";
for (char c : s) cout << c << ' '; // string:一次一個字元
cout << '\n';
return 0;
}
執行結果:
2 5 9
5 3 2 7
g o o d
它不是魔法——對 vector 來說,編譯器把它展開成 10.5 學過的 iterator 迴圈:
for (auto it = v.begin(); it != v.end(); it++) {
int x = *it;
// 迴圈本體
}
¶要修改元素:加 &
看清楚展開式的 int x = *it;——每一圈的 x 是元素的複本(上冊 7.5 傳值的既視感)。改 x 改的是複本,容器毫髮無傷。想真的動到元素,把 x 宣告成上一節的參考:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> v(n);
for (int &x : v) cin >> x; // 加 &:x 是元素的別名,讀入直接存進 v
for (int &x : v) x *= 2; // 全部乘 2
for (int x : v) cout << x << ' '; // 只讀不改:不加 & 也行
cout << '\n';
return 0;
}
執行結果(輸入 3 與 4 7 1):
8 14 2
搭配 12.2 就是高手程式碼裡的 for (auto &x : v)——元素型態長的時候讓編譯器推,& 照加。
¶range-based for 的極限
省力是有代價的,三件事它做不到:
- 拿不到索引值——迴圈裡只有元素,沒有「這是第幾格」。要印編號、要同時碰
v[i]和v[i + 1],回頭用傳統 for。 - 只能從頭走到尾——不能倒著走、不能跳著走、不能中途從第 k 格開始。
- 不能一邊走一邊改容器長度——邊掃邊
push_back會出事:容器變長時元素可能整批搬家(10.2 的抽象理解提過),迴圈手上記的位置會變成空號(UB)。
它是「全部看一遍」的專用工具;範圍一旦不是「全部」,就不是它的戲了。
動手試試看:把你解好多數列(簡單版)與相鄰城市列表的舊程式碼翻出來,用 range-based for 改寫所有「掃過整個 vector」的迴圈(讀入的迴圈記得加 &),重新提交確認照樣 AC。改完數數看少打了幾個字。