Spec-Zone.ru › C++

std::ranges::remove, std::ranges::remove_if

Определено в заголовочном файле <algorithm>
Вызов функции
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) (с 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) (с 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) (с 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) (с C++20)

Удаляет все элементы, удовлетворяющие определенным критериям, из диапазона [first, last) и возвращает поддиапазон [ret, last), где ret — итератор, указывающий на позицию за концом нового диапазона.

1) Удаляет все элементы, равные value, используя std::invoke(proj, *i) == value для сравнения.
3) Удаляет все элементы, для которых std::invoke(pred, std::invoke(proj, *i)) возвращает true.
2,4) Аналогично (1,3), но использует r в качестве диапазона, как если бы ranges::begin(r) использовалось как first и ranges::end(r) как last.

Удаление выполняется путём перемещения (с помощью присваивания перемещением) элементов в диапазоне таким образом, чтобы элементы, которые не подлежат удалению, оказались в начале диапазона. Относительный порядок оставшихся элементов сохраняется, и физический размер контейнера не изменяется. Итераторы, указывающие на элемент между новым логическим концом и физическим концом диапазона, всё ещё могут быть использованы для получения значения элемента, но сами элементы имеют неопределённые значения (в соответствии с MoveAssignable пост-условием).

Функциональные сущности, описанные на этой странице, являются niebloid, то есть:

  • Явные списки шаблонных аргументов указать нельзя при вызове любой из них.
  • Ни одна из них не видима для зависимого от аргументов поиска.
  • Если какая-либо из них найдена обычным поиском без указания области видимости как имя слева от оператора вызова функции, зависимый от аргументов поиск подавляется.

На практике они могут быть реализованы как объекты функций или с помощью специальных расширений компилятора.

Параметры

first, last - диапазон элементов для обработки
r - диапазон элементов для обработки
value - значение элементов для удаления
pred - предикат для применения к проецированным элементам
proj - проекция для применения к элементам

Возвращаемое значение

{ret, last}, где [first, ret) — полученный поддиапазон после удаления, а элементы в поддиапазоне [ret, last) находятся в корректном, но неопределённом состоянии.

Сложность

Точно N применений соответствующего предиката и любой проекции, где N = ranges::distance(first, last), и N - 1 операций перемещения в худшем случае.

Примечания

Вызов ranges::remove обычно следует за вызовом функции-члена контейнера erase, которая стирает неопределённые значения и уменьшает физический размер контейнера, чтобы он соответствовал его новому логическому размеру. Эти два вызова вместе составляют так называемый приём "удаление-удаление", который можно реализовать с помощью свободной функции std::erase , имеющей перегрузки для всех стандартных контейнеров последовательности, или std::erase_if , имеющей перегрузки для всех стандартных контейнеров.

Аналогично названные функции-члены контейнера list::remove, list::remove_if, forward_list::remove и forward_list::remove_if удаляют удалённые элементы.

Эти алгоритмы обычно не могут быть использованы с ассоциативными контейнерами, такими как std::set и std::map, потому что их типы итераторов не ссылаются на типы MoveAssignable (ключи в этих контейнерах не изменяемы).

Поскольку ranges::remove принимает value по ссылке, это может привести к неожиданному поведению, если это ссылка на элемент диапазона [first, last).

Возможная реализация

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 {};

Пример

#include <algorithm>
#include <cctype>
#include <iomanip>
#include <iostream>
#include <string>
#include <string_view>
 
int main()
{
    std::string v1{"No - Diagnostic - Required"};
    std::cout << std::quoted(v1) << " (v1, size: " << v1.size() << ")\n";
    const auto ret = std::ranges::remove(v1, ' ');
    std::cout << std::quoted(v1) << " (v1 after `remove`, size: " << 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 after `erase`, size: " << v1.size() << ")\n\n";
 
    // remove_if with custom unary predicate:
    auto rm = [](char c) { return !std::isupper(c); };
    std::string v2{"Substitution Failure Is Not An Error"};
    std::cout << std::quoted(v2) << " (v2, size: " << v2.size() << ")\n";
    const auto [first, last] = std::ranges::remove_if(v2, rm);
    std::cout << std::quoted(v2) << " (v2 after `remove_if`, size: " << v2.size() << ")\n";
    std::cout << ' ' << std::string(std::distance(v2.begin(), first), '^') << '\n';
    v2.erase(first, last);
    std::cout << std::quoted(v2) << " (v2 after `erase`, size: " << v2.size() << ")\n\n";
 
    // creating a view into a container that is modified by `remove_if`:
    for (std::string s : {"Small Object Optimization", "Non-Type Template Parameter"})
        std::cout << std::quoted(s) << " => "
            << std::string_view{begin(s), std::ranges::remove_if(s, rm).begin()} << '\n';
}

Возможный вывод:

"No _ Diagnostic _ Required" (v1, size: 26)
"No_Diagnostic_Requiredired" (v1 after `remove`, size: 26)
 ^^^^^^^^^^^^^^^^^^^^^^
"No_Diagnostic_Required" (v1 after `erase`, size: 22)
 
"Substitution Failure Is Not An Error" (v2, size: 36)
"SFINAEtution Failure Is Not An Error" (v2 after `remove_if`, size: 36)
 ^^^^^^
"SFINAE" (v2 after `erase`, size: 6)
 
"Small Object Optimization" => SOO
"Non-Type Template Parameter" => NTTP

См. также

ranges::remove_copyranges::remove_copy_if
(C++20)(C++20)
копирует диапазон элементов, опуская те, которые удовлетворяют определенным критериям
(niebloid)
ranges::unique
(C++20)
удаляет последовательные дублирующиеся элементы в диапазоне
(niebloid)
removeremove_if
удаляет элементы, удовлетворяющие определенным критериям
(шаблонная функция)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/algorithm/ranges/remove

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API