Spec-Zone.ru › C++

std::remove, std::remove_if

Определено в заголовке <algorithm>
(1)
template< class ForwardIt, class T >
ForwardIt remove( ForwardIt first, ForwardIt last, const T& value );
(до C++20)
template< class ForwardIt, class T >
constexpr ForwardIt remove( ForwardIt first, ForwardIt last, const T& value );
(с C++20)
template< class ExecutionPolicy, class ForwardIt, class T >
ForwardIt remove( ExecutionPolicy&& policy,
                  ForwardIt first, ForwardIt last, const T& value );
(2) (с C++17)
(3)
template< class ForwardIt, class UnaryPredicate >
ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPredicate p );
(до C++20)
template< class ForwardIt, class UnaryPredicate >
constexpr ForwardIt remove_if( ForwardIt first, ForwardIt last,
                               UnaryPredicate p );
(с C++20)
template< class ExecutionPolicy, class ForwardIt, class UnaryPredicate >
ForwardIt remove_if( ExecutionPolicy&& policy,
                     ForwardIt first, ForwardIt last, UnaryPredicate p );
(4) (с C++17)

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

1) Удаляет все элементы, равные value (используя operator==).
3) Удаляет все элементы, для которых предикат p возвращает true.
2,4) То же, что и (1,3), но выполняется в соответствии с policy. Эти перегрузки не участвуют в разрешении перегрузки, если

std::is_execution_policy_v<std::decay_t<ExecutionPolicy>> является true.

(до C++20)

std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> является true.

(с C++20)

Если тип значения ForwardIt не является CopyAssignable, поведение неопределено.

(до C++11)

Если тип *first не является MoveAssignable, поведение неопределено.

(с C++11)

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

Параметры

first, last - диапазон обрабатываемых элементов
value - значение удаляемых элементов
policy - используемая политика выполнения. Подробности см. в политике выполнения.
p - унарный предикат, возвращающий ​true, если элемент должен быть удалён.

Выражение p(v) должно быть приводимо к bool для каждого аргумента v типа (возможно, const) VT, где VT — тип значения ForwardIt, независимо от категории значения, и не должно изменять v. Таким образом, тип параметра VT& не допускается, также как и VT если для VT перемещение эквивалентно копированию(с C++11). ​

Требования к типу
-ForwardIt должно соответствовать требованиям LegacyForwardIterator.
-UnaryPredicate должно соответствовать требованиям Predicate.

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

Итератор, указывающий на конец нового диапазона значений (если это не end, то он указывает на неопределённое значение, так же как и итераторы на любые значения между этим итератором и end).

Сложность

При условии N как std::distance(first, last):

1,2) ровно N сравнений с value с использованием operator==.
3,4) ровно N применений предиката p.

Исключения

Перегрузки с параметром шаблона ExecutionPolicy сообщают об ошибках следующим образом:

  • Если при выполнении функции, вызываемой как часть алгоритма, возникает исключение и ExecutionPolicy — одна из стандартных политик, вызывается std::terminate. Для любой другой ExecutionPolicy, поведение неопределено.
  • Если алгоритм не может выделить память, выбрасывается std::bad_alloc.

Примечания

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

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

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

Стандартная библиотека также определяет перегрузку std::remove в <cstdio>, которая принимает const char* и используется для удаления файлов.

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

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

remove
template<class ForwardIt, class T>
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value)
{
    first = std::find(first, last, value);
    if (first != last)
        for (ForwardIt i = first; ++i != last;)
            if (!(*i == value))
                *first++ = std::move(*i);
    return first;
}
remove_if
template<class ForwardIt, class UnaryPredicate>
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p)
{
    first = std::find_if(first, last, p);
    if (first != last)
        for (ForwardIt i = first; ++i != last;)
            if (!p(*i))
                *first++ = std::move(*i);
    return first;
}

Пример

Следующий код удаляет все пробелы из строки, сдвигая все символы, отличные от пробела, влево, а затем удаляя лишние. Это пример идиомы удаления-удаления.

#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
#include <string_view>
 
int main()
{
    std::string str1{"Text with some   spaces"};
 
    auto noSpaceEnd = std::remove(str1.begin(), str1.end(), ' ');
 
    // The spaces are removed from the string only logically.
    // Note, we use view, the original string is still not shrunk:
    std::cout << std::string_view(str1.begin(), noSpaceEnd) 
              << " size: " << str1.size() << '\n';
 
    str1.erase(noSpaceEnd, str1.end());
 
    // The spaces are removed from the string physically.
    std::cout << str1 << " size: " << str1.size() << '\n';
 
    std::string str2 = "Text\n with\tsome \t  whitespaces\n\n";
    str2.erase(std::remove_if(str2.begin(), 
                              str2.end(),
                              [](unsigned char x) { return std::isspace(x); }),
               str2.end());
    std::cout << str2 << '\n';
}

Вывод:

Textwithsomespaces size: 23
Textwithsomespaces size: 18
Textwithsomewhitespaces

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применено к Поведение, как опубликовано Правильное поведение
LWG 283 C++98 T должен был быть EqualityComparable, но
тип значения ForwardIt не всегда T
требовалось, чтобы тип значения ForwardIt
был CopyAssignable вместо

См. также

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

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

Spec-Zone.ru

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