26-03-2014

Iterando sobre contenedores de C++

Si escribís código en C++ y usáis los contenedores de la STL (std::vector, std::list, etc.) probablemente estéis hartos de la forma de iterar sobre ellos:

for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it)

La vida es demasiado corta para estar escribiendo cabeceras de bucles de ese estilo, así que aquí viene una solución en forma de macro del preprocesador, cosa que no es muy buena idea, pero lo cuento más bien como proof of concept:

#define each(c, i, s) for (auto (i) = (c).begin(); (i) != (c).end(); ++(i)) {s;}

Esta macro recibe un contenedor, un nombre para el iterador y una serie de sentencias, de forma que se pueda ejecutar algo del tipo:

std::vector<int> v(20);

std::iota(v.begin(), v.end(), 1); // Rellena con valores de 1 a 20

each (v, it,
   std::cout << *it;
   std::cout << " ";
);

Y es válido para cualquier contenedor con iteradores y métodos begin() y end(), por lo que se puede usar con vectores, listas, mapas o clases propias. El código resultante es más abstracto y limpio, pero realmente una macro de preprocesador no es una buena forma de ahorrarnos código, ya que no son muy seguras y podemos caer en errores de sintaxis muy fácilmente.

La alternativa segura y correcta sería usar la STL, que incluye en la biblioteca <algorithm> una función for_each que permite hacer algo muy parecido:

std::for_each (v.begin(), v.end(), [](int &n) {
   std::cout << n;
   std::cout << " ";
});

Y por último, la manera más cómoda y familiar de recorrer un contenedor sería utilizar la nueva sintaxis de for incluida en el estándar C++11, similar a las de otros lenguajes:

for (int &n : v) { // Itera sobre cada elemento n en v
   std::cout << n << " ";
}

Documentación: for_each, range for