Bucles basados en rangos

Cuando iteramos sobre todos los elementos de una secuencia, como ocurre a menudo con el contenedor vector, indefectiblemente usamos un bucle for con las habituales características:

  • Inicialización de un contador en 0

  • Condición de finalización cuando el contador iguala el tamaño del contenedor

  • Incremento del contador en una unidad

    for (size_t i = 0; i < v.size(); ++i)
    

En esos casos es más robusto y simple utilizar un formato de bucle especializado en recorrer todos los elementos de una secuencia: un bucle for especializado en rangos.

La sintaxis es la siguiente:

for (tipo elemento : identificador_secuencia)

La variable elemento va tomando iteradamente los valores de la secuencia. Esta variable elemento es una copia del elemento del contenedor: si dentro del bucle alteramos el valor de la variable elemento, el contenedor permanece inalterado.

Nota

Este tipo de bucles basados en recorrer los elementos de una secuencia están presentes en muchos lenguajes, siendo masivamente utilizados en el lenguaje Python. En C++ se incorporaron a partir de la versión 11 del estándar.

Veamos un ejemplo sin y con bucle for por rango.

Suma de los elementos de un vector: sin usar for por rango

#include <iostream>
#include <vector>
using namespace std;

double suma_elementos(const vector<double>&);

int main ()
{
    vector<double> v{1, 2, 3, 4, 5};
    double  suma = suma_elementos(v);
    cout << "La suma de los elementos es " << suma << endl;
}

double suma_elementos(const vector<double>& v)
{
    double suma = 0.;
    for (size_t i = 0; i < v.size(); ++i)
        suma += v[i];
    return suma;
}

Edita, compila y ejecuta el código

Suma de los elementos de un vector: usando for por rango

#include <iostream>
#include <vector>
using namespace std;

double suma_elementos(const vector<double>&);

int main ()
{
    vector<double> v{1, 2, 3, 4, 5};
    double  suma = suma_elementos(v);
    cout << "La suma de los elementos es " << suma << endl;
}

double suma_elementos(const vector<double>& v)
{
    double suma = 0.;
    for (double x : v)
        suma += x;
    return suma;
}

Edita, compila y ejecuta el código

El especificador auto

Definir inicializando una variable con el resultado de una expresión o con el valor devuelto por una función es una tarea recurrente en programación.

En ocasiones, por ser un tipo verboso, porque resulta más cómodo o porque es más robusto, es mejor que sea el compilador el que deduzca automáticamente el tipo asociado a una expresión o el valor devuelto por una función en el momento de definir inicializando una variable.

Para ello, basta especificar la variable a almacenar como auto. En el caso del bucle for por rango, auto dota aún de mayor simplicidad a la construcción.

Veamos el ejemplo anterior, pero ahora usando masivamente auto.

Suma de los elementos de un vector: usando for por rango y auto

#include <iostream>
#include <vector>
using namespace std;

double suma_elementos(const vector<double>&);
int main ()
{
    vector<double> v{1, 2, 3, 4, 5};
    auto  suma = suma_elementos(v);
    cout << "La suma de los elementos es " << suma << endl;
}
double suma_elementos(const vector<double>& v)
{
    auto suma = 0.; // Deduce que es double porque 0. es un literal double
    for(auto x : v)
        suma += x;
    return suma;
}

Edita, compila y ejecuta el código

En el siguiente ejemplo se muestra comparativamente dos versiones de una función que muestra por pantalla los valores de un vector.

void muestra_vector_v1(const vector<double>& v)
{
    for (size_t i = 0; i < v.size(); ++i)
        cout << v[i] << endl;
}
void muestra_vector_v2(const vector<double>& v)
{
    for (auto x : v)
        cout << x << endl;
}