Подтвердить что ты не робот

Как удалить элементы из контейнеров STL?

Как стирать элементы из контейнеров STL, иметь указанное значение или удовлетворять некоторым условиям?

Существует ли один общий или единый способ сделать это для разных видов контейнеров?

4b9b3361

Ответ 1

К сожалению, нет ни одного унифицированного интерфейса или шаблона для стирания элементов из контейнеров STL. Но появляются три поведения:

std::vector Шаблон

Чтобы стереть элементы, удовлетворяющие определенному условию, из std:: vector, общий метод - это так называемый удалить-удалить idiom.

Если v - это экземпляр std:: vector, и мы хотим удалить из него элементы со значением x, код, подобный это можно использовать:

 //Стереть элементы, имеющие значение  "x"  из вектора  "v" 
v.erase(std:: remove (v.begin(), v.end(), x), v.end());
Код>

Если критерий, выполняемый для стирания элементов, более сложный, чем простой элемент , который должен быть удален == x ", алгоритм std:: remove_if() может использоваться вместо std:: remove():

 //Удалять элементы, соответствующие "erasing_condition" из вектора  "v" 
v.erase(std:: remove_if (v.begin(), v.end(), erasing_condition), v.end());
Код>

где erasing_condition - это унарный предикат, который может быть выражен в нескольких формах: например. это может быть bool -returning function, принимая тип векторного элемента как входной (поэтому, если возвращаемое значение true), элемент будет удален из вектор, если это false, это не будет); или он может быть выражен в строке как лямбда; это может быть функтор; и др.

(Оба std:: remove() и std:: remove_if() являются общими алгоритмами из заголовка <algorithm>.)

Вот ясное объяснение из Википедии:

Библиотека algorithm предоставляет remove и remove_ifалгоритмы для этого. Поскольку эти алгоритмы работают в диапазоне элементы, обозначенные двумя итераторами вперед, они не знают основного контейнера или коллекции. Таким образом, никакие элементы на самом деле удалены из контейнера. Скорее, все элементы, которые не соответствуют критерии удаления сводятся к фронту диапазона, в такой же относительный порядок. Остальные элементы остаются в действительном, но неуказанное состояние. Когда это будет сделано, remove возвращает итератор указывая один за последним неуправляемым элементом.

Чтобы фактически удалить элементы из контейнера, remove объединяется с функцией члена erase контейнера, следовательно, имя "erase-remove idiom".

В основном, std:: remove() и std:: remove_if() compact элементы, которые выполняют not, удовлетворяют критериям стирания фронт диапазона (т.е. к началу vector), а затем erase() фактически удаляет оставшиеся элементы из контейнера.

Этот шаблон применяется также к другим контейнерам, таким как std:: deque.

std:: list Pattern

Чтобы стереть элементы из std:: list, просто remove() и remove_if():

 //Стереть элементы, имеющие значение  "x"  из списка  "l" 
l.remove(x)

// Удаление элементов, удовлетворяющих условию "erasing_condition" из списка  "l" 
l.remove_if (erasing_condition);
Код>

(Где erasing_condition является унарным предикатом, с теми же характеристиками, которые обсуждались для std:: remove_if() в приведенном выше разделе.)

Такой же шаблон может быть применен к аналогичным контейнерам, например std:: forward_list.

Ассоциативные контейнеры (например, std:: map, std:: set,...) Шаблон

Ассоциативные контейнеры, такие как std:: map, std:: set, std:: unordered_map и т.д. следуйте общей схеме, описанной здесь:

  • Если условие стирания является простым сопоставлением клавиш (т.е. "стирает элемент имея ключ x "), можно вызвать простой метод erase():

     //Удалить элемент с ключом "k" из карты "m":
    m.erase(k);
    Код>
  • Если условие стирания является более сложным и выражается некоторыми обычными унарный предикат (например, "стереть все нечетные элементы" ), тогда может использоваться цикл для (с явной проверкой состояния стирания в теле цикла и вызовом метода erase (iterator)):

    <Предварительно > <код > // // Удаление всех элементов из ассоциативного контейнера "c", удовлетворяющего "erasing_condition" : // для (auto it = c.begin(); it!= c.end();/* "it" обновлено внутри тела цикла */) {   if (erasing_condition (* it))   {       // Стереть элемент, соответствующий указанному условию       // из ассоциативного контейнера.       it = c.erase(it);       // Заметка:       //erase () возвращает итератор элементу       //, который следует за последним удаленным элементом,       // поэтому мы можем продолжить итерацию цикла "for" из этой позиции.   }   еще   {       // Текущий элемент does_not_ удовлетворяет условию стирания,       // поэтому мы можем просто перейти к следующему элементу.       ++ это;   } } Код >

Потребность в едином подходе

Как можно заметить из приведенного выше анализа, к сожалению, нет единого подхода к стиранию элементов из контейнеров STL.

В следующей таблице приведены вышеупомянутые шаблоны:

<Предварительно > <код > ---------------- + --------------------------- ---------------  Контейнер | Стирание шаблона ---------------- + --------------------------------- ---------               |  вектор | Используйте erase-remove idiom.  deque |               | ---------------- + --------------------------------- ---------               |  список | Вызовите методы remove()/remove_if().  forward_list |               | ---------------- + --------------------------------- ---------               |  карта | Простое удаление метода (ключа),  набор | или  unordered_map | петля через контейнер,  multimap | и стирание вызова (итератор) при сопоставлении               | состояние.  ... |               | ---------------- + --------------------------------- --------- Код >

Написание другого конкретного кода на основе конкретного контейнера подвержено ошибкам, трудно поддерживается, трудно читается и т.д.

Тем не менее, можно создавать шаблоны функций с общими именами (например, erase() и erase_if()) перегруженные для разных типов контейнеров, и вставлять вышеупомянутые реализации шаблонов в эти функции.
Таким образом, клиент может просто вызвать эти erase() и erase_if() общие функции, а компилятор отправит вызов в правильную реализацию (в время компиляции) на основе типа контейнера.

Представлен более элегантный подход с использованием метода метапрограмм шаблона от Stephan T. Lavavej здесь.