基於範圍的
Version >= C++ 11
for
迴圈可用於迭代基於迭代器的範圍的元素,而無需使用數字索引或直接訪問迭代器:
vector<float> v = {0.4f, 12.5f, 16.234f};
for(auto val: v)
{
std::cout << val << " ";
}
std::cout << std::endl;
這將迭代 v
中的每個元素,val
獲取當前元素的值。以下宣告:
for (for-range-declaration : for-range-initializer ) statement
相當於:
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr, __end = end-expr;
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
Version >= C++ 17
{
auto&& __range = for-range-initializer;
auto __begin = begin-expr;
auto __end = end-expr; // end is allowed to be a different type than begin in C++17
for (; __begin != __end; ++__begin) {
for-range-declaration = *__begin;
statement
}
}
這一變化是為 C++ 20 中 Ranges TS 的計劃支援而引入的。
在這種情況下,我們的迴圈相當於:
{
auto&& __range = v;
auto __begin = v.begin(), __end = v.end();
for (; __begin != __end; ++__begin) {
auto val = *__begin;
std::cout << val << " ";
}
}
請注意,auto val
宣告瞭一個值型別,它將是儲存在該範圍內的值的副本(我們正在從迭代器中複製初始化它)。如果儲存在該範圍內的值複製起來很昂貴,你可能需要使用 const auto &val
。你也不需要使用 auto
; 你可以使用適當的型別名稱,只要它可以從範圍的值型別中隱式轉換。
如果你需要訪問迭代器,基於範圍的對你來說無法幫助你(至少不需要付出一些努力)。
如果你想參考它,你可以這樣做:
vector<float> v = {0.4f, 12.5f, 16.234f};
for(float &val: v)
{
std::cout << val << " ";
}
如果你有 const
容器,你可以迭代 const
參考:
const vector<float> v = {0.4f, 12.5f, 16.234f};
for(const float &val: v)
{
std::cout << val << " ";
}
當序列迭代器返回代理物件並且你需要以非 const
方式對該物件進行操作時,可以使用轉發引用。注意:它很可能會使讀者感到困惑。
vector<bool> v(10);
for(auto&& val: v)
{
val = true;
}
提供給基於範圍的 for
的範圍型別可以是以下之一:
-
語言陣列:
float arr[] = {0.4f, 12.5f, 16.234f}; for(auto val: arr) { std::cout << val << " "; }
請注意,分配動態陣列不計算:
float *arr = new float[3]{0.4f, 12.5f, 16.234f}; for(auto val: arr) //Compile error. { std::cout << val << " "; }
-
任何具有成員函式
begin()
和end()
的型別,它將迭代器返回給該型別的元素。標準庫容器符合條件,但也可以使用使用者定義的型別:struct Rng { float arr[3]; // pointers are iterators const float* begin() const {return &arr[0];} const float* end() const {return &arr[3];} float* begin() {return &arr[0];} float* end() {return &arr[3];} }; int main() { Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }
-
任何具有非成員
begin(type)
和end(type)
函式的型別,可以通過引數依賴查詢找到,基於type
。這對於建立範圍型別非常有用,而無需修改類型別本身:namespace Mine { struct Rng {float arr[3];}; // pointers are iterators const float* begin(const Rng &rng) {return &rng.arr[0];} const float* end(const Rng &rng) {return &rng.arr[3];} float* begin(Rng &rng) {return &rng.arr[0];} float* end(Rng &rng) {return &rng.arr[3];} } int main() { Mine::Rng rng = {{0.4f, 12.5f, 16.234f}}; for(auto val: rng) { std::cout << val << " "; } }