◐ Shell
clean mode source ↗

std::ranges::remove, std::ranges::remove_if - cppreference.com

Definido en el archivo de encabezado <algorithm>

Signatura de la llamada

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> remove( I first, S last, const T& value, Proj proj = {} );

(1) (desde C++20)

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> remove( R&& r, const T& value, Proj proj = {} );

(2) (desde C++20)

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> remove_if( I first, S last, Pred pred, Proj proj = {} );

(3) (desde C++20)

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> remove_if( R&& r, Pred pred, Proj proj = {} );

(4) (desde C++20)

Elimina todos los elementos que satisfacen un criterio específico del rango [firstlast) y devuelve un subrango [retlast), 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:

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 [firstret) es el subrango resultante después de la eliminación, y los elementos en el subrango [retlast) 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 [firstlast).

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

Véase también