基于范围的

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