C++ Basado en rango para


Ejemplo

C ++ 11

for bucles pueden utilizarse para recorrer en iteración los elementos de un rango basado en iteradores, sin usar un índice numérico o acceder directamente a los iteradores:

vector<float> v = {0.4f, 12.5f, 16.234f};

for(auto val: v)
{
    std::cout << val << " ";
}

std::cout << std::endl;

Esto iterará sobre cada elemento en v , con val obteniendo el valor del elemento actual. La siguiente declaración:

for (for-range-declaration : for-range-initializer ) statement

es equivalente a:

{
    auto&& __range = for-range-initializer;
    auto __begin = begin-expr, __end = end-expr;
    for (; __begin != __end; ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}
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
    }
}

Este cambio se introdujo para el soporte planificado de Ranges TS en C ++ 20.

En este caso, nuestro bucle es equivalente a:

{
    auto&& __range = v;
    auto __begin = v.begin(), __end = v.end();
    for (; __begin != __end; ++__begin) {
        auto val = *__begin;
        std::cout << val << " ";
    }
}

Tenga en cuenta que auto val declara un tipo de valor, que será una copia de un valor almacenado en el rango (lo estamos inicializando desde el iterador). Si los valores almacenados en el rango son costosos de copiar, es posible que desee utilizar const auto &val . Tampoco está obligado a utilizar auto ; puede usar un nombre de tipo apropiado, siempre que sea implícitamente convertible del tipo de valor del rango.

Si necesita acceso al iterador, basado en rango no puede ayudarlo (no sin esfuerzo, al menos).

Si desea referenciarlo, puede hacerlo:

vector<float> v = {0.4f, 12.5f, 16.234f};

for(float &val: v)
{
    std::cout << val << " ";
}

Podría iterar en la referencia const si tiene un contenedor const :

const vector<float> v = {0.4f, 12.5f, 16.234f};

for(const float &val: v)
{
    std::cout << val << " ";
}

Se utilizarían referencias de reenvío cuando el iterador de secuencia devuelva un objeto proxy y usted necesite operar en ese objeto de forma no const . Nota: lo más probable es que confunda a los lectores de su código.

vector<bool> v(10);

for(auto&& val: v)
{
    val = true;
}

El tipo de "gama" proporcionada al intervalo basado- for puede ser uno de los siguientes:

  • Matrices de idiomas:

    float arr[] = {0.4f, 12.5f, 16.234f};
    
    for(auto val: arr)
    {
        std::cout << val << " ";
    }
    

    Tenga en cuenta que la asignación de una matriz dinámica no cuenta:

    float *arr = new float[3]{0.4f, 12.5f, 16.234f};
    
    for(auto val: arr) //Compile error.
    {
        std::cout << val << " ";
    }
    
  • Cualquier tipo que tenga funciones miembro begin() y end() , que devuelve los iteradores a los elementos del tipo. Los contenedores de la biblioteca estándar califican, pero los tipos definidos por el usuario también se pueden usar:

    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 << " ";
        }
    }
    
  • Cualquier tipo que tenga funciones de begin(type) y end(type) que no sean miembros que se pueden encontrar mediante búsqueda dependiente de argumento, según el type . Esto es útil para crear un tipo de rango sin tener que modificar el tipo de clase en sí:

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