std::ranges::remove, std::ranges::remove_if - cppreference.com
| Definido en el archivo de encabezado |
||
| Signatura de la llamada |
||
|
(1) | (desde C++20) |
|
|
(2) | (desde C++20) |
|
(3) | (desde C++20) |
|
|
(4) | (desde C++20) |
Elimina todos los elementos que satisfacen un criterio específico del rango [first, last) y devuelve un subrango [ret, last), donde ret es un iterador pasado el final para el nuevo final del rango.
1) Elimina todos los elementos que son iguales a value, usando std::invoke(proj, *i) == value para comparar.
3) Elimina todos los elementos para los que std::invoke(pred, std::invoke(proj, *i)) devuelve true.
2,4) Igual que (1,3), pero usa r como el rango, como si usara ranges::begin(r) como first y ranges::end(r) como last.
La eliminación se realiza desplazando (mediante la asignación de movimiento) los elementos del rango de forma que los elementos que no se van a eliminar aparezcan al principio del rango. Se conserva el orden relativo de los elementos que quedan y el tamaño: físico del contenedor no cambia. Los iteradores que apuntan a un elemento entre el nuevo final lógico y el final físico del rango siguen siendo desreferenciables, pero los elementos en sí tienen valores no especificados (según la poscondición de MoveAssignable).
Las entidades similares a funciones descritas en esta página son niebloids, es decir:
- Las listas de argumentos de plantilla explícitas no se pueden especificar al llamar a cualquiera de ellas.
- Ninguna de ellas es visible para la búsqueda dependiente de argumentos.
- Cuando alguna de ellas se encuentra mediante la búsqueda normal no calificada como el nombre a la izquierda del operador de llamada a función, se inhibe la búsqueda dependiente de argumentos.
En la práctica, pueden implementarse como objetos función o con extensiones de compilador especiales.
Parámetros
| first, last | - | El rango de elementos a procesar. |
| r | - | El rango de elementos a procesar. |
| value | - | El valor de los elementos a eliminar.. |
| pred | - | El predicado para aplicar a los elementos proyectados. |
| proj | - | La proyección para aplicar a los elementos. |
Valor de retorno
{ret, last}, donde [first, ret) es el subrango resultante después de la eliminación, y los elementos en el subrango [ret, last) están todos en un estado válido pero no especificado.
Complejidad
Exactamente N aplicaciones del predicado correspondiente y cualquier proyección, donde N = ranges::distance(first, last), y N - 1 operaciones de movimiento en el peor de los casos.
Notas
Una llamada a ranges::remove suele ir seguida de una llamada a la función miembro erase de un contenedor, que borra los valores no especificados y reduce el tamaño: físico del contenedor para que coincida con su nuevo tamaño lógico. Estas dos invocaciones juntas constituyen el llamado modismo Erase–remove, que se puede lograr mediante la función libre std::erase que tiene sobrecargas para todos los contenedores estándar de secuencia, o std::erase_if que tiene sobrecargas para todos los contenedores estándar}}.
Las funciones miembro de contenedor de nombre similar, list::remove, list::remove_if, forward_list::remove, y forward_list::remove_if borran los elementos eliminados.
Estos algoritmos no se pueden usar con contenedores asociativos como std::set y std::map porque sus tipos iterador no eliminan la referencia a los tipos MoveAssignable (las claves en estos contenedores no son modificables).
Debido a que std::remove toma value por referencia, puede tener un comportamiento inesperado si es una referencia a un elemento del rango [first, last).
Posible implementación
| remove |
|---|
struct remove_fn { template<std::permutable I, std::sentinel_for<I> S, class T, class Proj = std::identity> requires std::indirect_binary_predicate< ranges::equal_to, std::projected<I, Proj>, const T*> constexpr ranges::subrange<I> operator()(I first, S last, const T& value, Proj proj = {}) const { first = ranges::find(std::move(first), last, value, proj); if (first != last) { for (I i {std::next(first)}; i != last; ++i) { if (value != std::invoke(proj, *i)) { *first = ranges::iter_move(i); ++first; } } } return {first, last}; } template<ranges::forward_range R, class T, class Proj = std::identity> requires std::permutable<ranges::iterator_t<R>> && std::indirect_binary_predicate< ranges::equal_to, std::projected< ranges::iterator_t<R>, Proj>, const T*> constexpr ranges::borrowed_subrange_t<R> operator()(R&& r, const T& value, Proj proj = {}) const { return (*this)(ranges::begin(r), ranges::end(r), value, std::move(proj)); } }; inline constexpr remove_fn remove {}; |
| remove_if |
struct remove_if_fn { template<std::permutable I, std::sentinel_for<I> S, class Proj = std::identity, std::indirect_unary_predicate<std::projected<I, Proj>> Pred> constexpr ranges::subrange<I> operator()(I first, S last, Pred pred, Proj proj = {}) const { first = ranges::find_if(std::move(first), last, pred, proj); if (first != last) { for (I i {std::next(first)}; i != last; ++i) { if (!std::invoke(pred, std::invoke(proj, *i))) { *first = ranges::iter_move(i); ++first; } } } return {first, last}; } template<ranges::forward_range R, class Proj = std::identity, std::indirect_unary_predicate< std::projected<ranges::iterator_t<R>, Proj>> Pred> requires std::permutable<ranges::iterator_t<R>> constexpr ranges::borrowed_subrange_t<R> operator()(R&& r, Pred pred, Proj proj = {}) const { return (*this)(ranges::begin(r), ranges::end(r), pred, std::move(proj)); } }; inline constexpr remove_if_fn remove_if {}; |
Ejemplo
#include <algorithm> #include <cctype> #include <iomanip> #include <iostream> #include <string> #include <string_view> int main() { std::string v1 {"No - se requiere - diagnóstico"}; std::cout << std::quoted(v1) << " (v1, tamaño: " << v1.size() << ")\n"; const auto ret = std::ranges::remove(v1, ' '); std::cout << std::quoted(v1) << " (v1 después de `remove`, tamaño: " << v1.size() << ")\n"; std::cout << ' ' << std::string(std::distance(v1.begin(), ret.begin()), '^') << '\n'; v1.erase(ret.begin(), ret.end()); std::cout << std::quoted(v1) << " (v1 después de `erase`, tamaño: " << v1.size() << ")\n\n"; // remove_if con predicado unario personalizado: auto rm = [](char c) { return !std::isupper(c); }; std::string v2 {"Falla En La Sustitución No Es Un Error"}; std::cout << std::quoted(v2) << " (v2, tamaño: " << v2.size() << ")\n"; const auto [first, last] = std::ranges::remove_if(v2, rm); std::cout << std::quoted(v2) << " (v2 después de `remove_if`, tamaño: " << v2.size() << ")\n"; std::cout << ' ' << std::string(std::distance(v2.begin(), first), '^') << '\n'; v2.erase(first, last); std::cout << std::quoted(v2) << " (v2 después de `erase`, tamaño: " << v2.size() << ")\n\n"; // se crea una vista sobre cadena dentro de un contenedor que se modifica por `remove_if`: for (std::string s : {"Optimización de Objeto Pequeño", "Parámetro de Plantilla de No Tipo"}) std::cout << std::quoted(s) << " => " << std::string_view {begin(s), std::ranges::remove_if(s, rm).begin()} << '\n'; }
Posible salida:
"No - se requiere - diagnóstico" (v1, tamaño: 31) "No-serequiere-diagnósticostico" (v1 después de `remove`, tamaño: 31) ^^^^^^^^^^^^^^^^^^^^^^^^^^ "No-serequiere-diagnóstico" (v1 después de `erase`, tamaño: 26) "Falla En La Sustitución No Es Un Error" (v2, tamaño: 39) "FELSNEUE La Sustitución No Es Un Error" (v2 después de `remove_if`, tamaño: 39) ^^^^^^^^ "FELSNEUE" (v2 después de `erase`, tamaño: 8) "Optimización de Objeto Pequeño" => OOP "Parámetro de Plantilla de No Tipo" => PPNT