Spec-Zone.ru › C++

std::remove, std::remove_if

Определено в заголовочном файле <algorithm>
template< class ForwardIt, class T >
ForwardIt remove( ForwardIt first, ForwardIt last, const T& value );
(1) (до C++26)
(constexpr начиная с C++20)
template< class ForwardIt, class T = typename std::iterator_traits
                                         <ForwardIt>::value_type >
constexpr ForwardIt remove( ForwardIt first, ForwardIt last,
                            const T& value );
(начиная с C++26)
template< class ExecutionPolicy, class ForwardIt, class T >
ForwardIt remove( ExecutionPolicy&& policy,
                  ForwardIt first, ForwardIt last, const T& value );
(2) (начиная с C++17)
(до C++26)
template< class ExecutionPolicy, class ForwardIt,
          class T = typename std::iterator_traits
                        <ForwardIt>::value_type >
ForwardIt remove( ExecutionPolicy&& policy,
                  ForwardIt first, ForwardIt last, const T& value );
(начиная с C++26)
template< class ForwardIt, class UnaryPred >
ForwardIt remove_if( ForwardIt first, ForwardIt last, UnaryPred p );
(3) (constexpr начиная с C++20)
template< class ExecutionPolicy, class ForwardIt, class UnaryPred >
ForwardIt remove_if( ExecutionPolicy&& policy,
                     ForwardIt first, ForwardIt last, UnaryPred p );
(4) (начиная с C++17)

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

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

std::is_execution_policy_v<std::decay_t<ExecutionPolicy>>

(до C++20)

std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>>

(начиная с C++20)

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

(до C++11)

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

(начиная с C++11)

Пояснение

Удаление выполняется путём сдвига элементов диапазона таким образом, чтобы элементы, которые не нужно удалять, оказались в начале диапазона.

  • Сдвиг выполняется с помощью оператора копирующего присваивания(до C++11)оператора перемещающего присваивания(начиная с C++11).
  • Операция удаления является стабильной: относительный порядок элементов, которые не нужно удалять, сохраняется.
  • Базовая последовательность [first, last) не укорачивается в результате операции удаления. Пусть result — возвращённый итератор:
    • Все итераторы в [result, last) по-прежнему допускают разыменование.
  • Каждый элемент [result, last) находится в корректном, но неуточнённом состоянии, поскольку перемещающее присваивание может устранить элементы, переместив значения из элементов, изначально находившихся в этом диапазоне.
(начиная с 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).

Сложность

Пусть \(\scriptsize N\)N — это std::distance(first, last):

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

Исключения

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

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

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

remove (1)
template<class ForwardIt, class T = typename std::iterator_traits<ForwardIt>::value_type>
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 (3)
template<class ForwardIt, class UnaryPred>
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPred 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;
}

Примечания

За вызовом remove обычно следует вызов функции-члена контейнера erase, которая фактически удаляет элементы из контейнера. Эти два вызова вместе образуют так называемый идиом erase-remove.

Того же эффекта можно добиться с помощью следующих нешаблонных функций:

  • 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>, которая принимает constchar* и используется для удаления файлов.

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

Макрос проверки возможностей Значение Стандарт Возможность
__cpp_lib_algorithm_default_value_type 202403 (C++26) Инициализация списком для алгоритмов (1,2)

Пример

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

#include <algorithm>
#include <cassert>
#include <cctype>
#include <complex>
#include <iomanip>
#include <iostream>
#include <string>
#include <string_view>
#include <vector>

int main()
{
    std::string str1{"Quick  Red  Dog"};
    std::cout << "1) " << std::quoted(str1) << '\n';
    const auto noSpaceEnd = std::remove(str1.begin(), str1.end(), ' ');
    std::cout << "2) " << std::quoted(str1) << '\n';

    // The spaces are removed from the string only logically.
    // Note, we use view, the original string is still not shrunk:
    std::cout << "3) " << std::quoted(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 << "4) " << std::quoted(str1) << ", size: " << str1.size() << '\n';
 
    std::string str2 = "Jumped\n Over\tA\vLazy \t  Fox\r\n";
    str2.erase(std::remove_if(str2.begin(), 
                              str2.end(),
                              [](unsigned char x) { return std::isspace(x); }),
               str2.end());
    std::cout << "5) " << std::quoted(str2) << '\n';
 
    std::vector<std::complex<double>> nums{{2, 2}, {1, 3}, {4, 8}};
    #ifdef __cpp_lib_algorithm_default_value_type
        nums.erase(std::remove(nums.begin(), nums.end(), {1, 3}), nums.end());
    #else
        nums.erase(std::remove(nums.begin(), nums.end(), std::complex<double>{1, 3}),
                   nums.end());
    #endif
    assert((nums == std::vector<std::complex<double>>{{2, 2}, {4, 8}}));
}

Вывод:

1) "Quick  Red  Dog"
2) "QuickRedDog Dog"
3) "QuickRedDog", size: 15
4) "QuickRedDog", size: 11
5) "JumpedOverALazyFox"

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

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

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

См. также

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

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

Spec-Zone.ru

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