基於 range 的 for 迴圈和 auto
C++11 引入一種迴圈的新形式,叫基於 range 的 for 迴圈,它允許我們用更簡單易讀的形式遍歷容器中的所有元素
vector<int> v{1, 2, 3};
for (int i : v) {
cout << i << endl;
}
可以使用 auto 來讓編譯器來推導元素的型別,上面的迴圈可以改寫為
for (auto i : v) {
cout << i << endl;
}
根據 auto 的推導規則,推匯出的型別是初始值退化後的型別,即
- 去掉引用
- 去掉 const、volatile 限定符
- 函式和陣列將變為指標
根據這個規則,上面迴圈推匯出的型別應該是 int,這對於 int 這種標量型別可能沒有問題,但如果容器裡存的是類型別,就可能帶來巨大的拷貝開銷,因為每次做迴圈都需要建立容器元素的區域性副本,這種情況下,應該用 auto &
for (auto& elem : container) // capture by (non-const) reference
這種形式中修改 elem 將影響容器的內容
對於模板程式碼,總是應該用這種形式,因為你沒法確定模板型別的拷貝開銷是否廉價
如果是隻讀的,還應該給 auto 加上 const 限定符
for (const auto& elem : container) // capture by const reference
代理迭代器
如果容器使用“代理迭代器”(比如 std::vector<bool>
),應該使用
for (auto&& elem : container) // capture by &&
假設我們想要用 range-for 遍歷一個 std::vector<bool>
並修改它的元素
vector<bool> v = {true, false, false, true};
for (auto& x : v)
x = !x;
會發現上面這段程式碼無法通過編譯,因為 std::vector
模板對 bool 型別做了模板特化,對 bool 元素做了打包處理以壓縮空間(把 8 個布林值存到一個位元組裡)
由於你無法返回一個 bit 的引用,std::vector<bool>
用了一種叫“代理迭代器”的模式
代理迭代器是一種迭代器,當它被解引用時,它不產生原始的 bool &
,而是返回一個臨時物件,它是可以轉換為 bool 的代理類
為了對 std::vector<bool>
使用 range-for 語法,必須使用 auto&&
來引用 bool 元素(關於 auto && 的推導規則請看這篇)
這種語法對於沒有使用代理迭代器的容器也適用,因此在泛型程式碼裡,最好的選擇就是用這種形式來遍歷修改容器元素