基於範圍的

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 << " ";
        }
    }