Английская Википедия:Erase–remove idiom

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

The erase–remove idiom is a common C++ technique to eliminate elements that fulfill a certain criterion from a C++ Standard Library container.[1][2][3]

Motivation

A common programming task is to remove all elements that have a certain value or fulfill a certain criterion from a collection. In C++, this can be achieved using a hand-written loop. It is, however, preferable to use an algorithm from the C++ Standard Library for such tasks.[1][2][3]

The member function erase can be used to delete an element from a collection, but for containers which are based on an array, such as vector, all elements after the deleted element have to be moved forward to avoid "gaps" in the collection. Calling erase multiple times on the same container generates much overhead from moving the elements.

The algorithm library provides the remove and remove_if algorithms for this. Because these algorithms operate on a range of elements denoted by two forward iterators, they have no knowledge of the underlying container or collection.[1][4]

These algorithms do not remove elements from the container, but move all elements that do not fit the removal criteria to the front of the range, keeping the relative order of the elements. This is done in a single pass through the data range.

As no elements are actually removed and the container retains the same size, the tail of the array has a length equal to the number of "removed" items; these items remain in memory but in an unspecified state. remove returns an iterator pointing to the first of these tail elements so that they can be deleted using a single call to erase.

Doing the same using only erase results in as many passes as there are elements to remove. For each of these passes, all elements after the erased element have to be moved, which is more time-consuming than shifting elements in a single pass.

C++20

As of C++20, the free functions std::erase and std::erase_if are provided for STL containers. These convenience functions can be used to perform correct erasure of elements without requiring the programmer to explicitly use the erase-remove idiom.[5]

Limitation

The erase–remove idiom cannot be used for containers that return const_iterator (e.g.: set)[6]

std::remove and/or std::remove_if do not maintain elements that are removed (unlike std::partition, std::stable_partition). Thus, erase–remove can only be used with containers holding elements with full value semantics without incurring resource leaks.[7]

Example

Compiler Explorer

// Use g++ -std=c++11 or clang++ -std=c++11 to compile.

#include <algorithm>  // remove and remove_if
#include <iostream>
#include <vector>

void Print(const std::vector<int>& vec) {
  for (auto val : vec) {
    std::cout << val << ' ';
  }
  std::cout << '\n';
}

int main() {
  std::vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  Print(v);

  // Removes all elements with the value 5.
  v.erase(std::remove(v.begin(), v.end(), 5), v.end());
  Print(v);

  // Removes all odd numbers.
  v.erase(std::remove_if(v.begin(), v.end(), [](int val) { return val & 1; }),
          v.end());
  Print(v);
}

/*
Output:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 6 7 8 9
0 2 4 6 8
*/

References

Шаблон:Reflist

  1. 1,0 1,1 1,2 Ошибка цитирования Неверный тег <ref>; для сносок EffSTL не указан текст
  2. 2,0 2,1 Ошибка цитирования Неверный тег <ref>; для сносок CS не указан текст
  3. 3,0 3,1 Ошибка цитирования Неверный тег <ref>; для сносок DDJ не указан текст
  4. Ошибка цитирования Неверный тег <ref>; для сносок Josuttis не указан текст
  5. Ошибка цитирования Неверный тег <ref>; для сносок cppref_vector_erase не указан текст
  6. Ошибка цитирования Неверный тег <ref>; для сносок Erase–remove idiom with std::set не указан текст
  7. Ошибка цитирования Неверный тег <ref>; для сносок effective_stl не указан текст