- Concepts
- Lambdas 'templatizadas'
- Funciones std::erase y std::erase_if
- std::jthread
- CPOs (customization point objects)
Consideremos el problema de eliminar de un contenedor estándar todos aquellos elementos que cumplan un criterio específico, bien porque éstos se comparen igual a un cierto valor dado o porque cumplan un determinado predicado unario. De emplear estándares previos a C++20, la consecución de una acción de este tipo en contenedores como std::vector, std::string o std::deque requeriría el uso de la solución idiomática conocida bajo el nombre de erase-remove [1]. En contraste, listas como std::list o std::forward_list permitirían usar funciones miembro públicas de eliminación específicas.
Así, por ejemplo, tomemos un vector de números enteros en el que queramos deshacernos de todas aquellas entradas que sean impares. La solución erase-remove requeriría:
- Aplicar el algoritmo no-miembro std::remove_if proporcionado en <algorithm> para trasladar al frente del vector todos aquellos elementos que no cumplan la condición de eliminación, expresada mediante un predicado unario [2]. Esta acción se lleva a cabo a través de operaciones de movimiento std::move, manteniendo el orden relativo original de los elementos supervivientes. El algoritmo retorna un iterador representando el nuevo end() lógico del vector. Sin embargo, todos los elementos comprendidos entre dicho iterador y el end() físico original del vector siguen estando ocupados y contribuyen aún al tamaño físico del contenedor, que no se ha visto modificado (ver figura inferior). Dichos elementos remanentes poseen, en general, valores inespecíficos.
- Tan sólo resta realizar una llamada a la función miembro pública erase() del vector con el fin de eliminar dichas entradas de valor inespecífico localizadas entre el nuevo end() lógico y el end() físico original, reduciendo de forma efectiva el tamaño del vector a su nueva longitud lógica.
El código anterior contrasta con el que programaríamos con el fin de eliminar números impares en una lista std::list de enteros. En efecto, este contenedor cuenta con su propia función miembro pública de eliminación remove_if(), de forma que el código siguiente resultaría más eficiente que el proporcionado por un esquema erase-remove como el anterior:
Solución unificada
El estándar C++20 proporciona sobrecargas de un nuevo algoritmo no-miembro de nombre std::erase_if() en las cabeceras <string>, <deque>, <forward_list>, <list>, <vector>, <map>, <set>, <unordered_map> y <unordered_set>. Dicho algoritmo permite adoptar una sintaxis común en los casos contemplados anteriormente, convirtiendo en obsoleta la solución idomática erase-remove en contenedores como el vector.
Así, en el ejemplo de eliminación de números impares en un vector de enteros, podríamos ahora escribir simplemente:
Este código resulta equivalente a la aplicación de la solución erase-remove analizada en la sección anterior.
Por consistencia, un esquema análogo podría ser también aplicado a la lista std::list:
Este código resulta equivalente a realizar una llamada a la función miembro pública remove_if() de la lista, por lo que no conlleva pérdida alguna de eficiencia.
Aun cuando no haya sido empleado en los ejemplos anteriores, conviene indicar que el algoritmo std::erase_if() devuelve un entero sin signo que contabiliza el número total de elementos eliminados. En efecto, en el caso del vector, la signatura de la función toma la forma [3]:
La signatura es similar para el resto de contenedores. Así, en el ejemplo del mapa que liga títulos de libros y años de publicación, podríamos codificar:
Los contenedores secuenciales <deque>, <forward_list>, <list>, <string> y <vector> vienen acompañados, adicionalmente, por un algoritmo no-miembro std::erase() para eliminar elementos que se comparen iguales a un cierto valor proporcionado. A modo de ejemplo, el código siguiente elimina todas las entradas de un vector de números enteros que sean iguales a 2:
- Scott Meyers, Effective STL. Addison-Wesley, 2001.
- Cppreference - std::remove, std::remove_if - https://en.cppreference.com/w/cpp/algorithm/remove
- Cppreference - std::erase, std::erase_if (std::vector) - https://en.cppreference.com/w/cpp/container/vector/erase2
No hay comentarios:
Publicar un comentario