Почему нет std :: erase?

При чтении STL я понял, что нет std::erase. Я не совсем уверен, почему его там нет. Допустимый пример использования следующий

std::vector<int> odd { 1, 3, 5, 3, 9, 11, 5, 17 };
std::sort(odd.begin(), odd.end());
std::erase(std::unique(odd.begin(), odd.end()), odd.end());

Однако он встроен в каждый контейнер. Если производительность является причиной, таким образом, что если объекты смежны, они могут быть удалены одним выстрелом. Но я думаю, что это выполнимо с помощью специализации шаблона.

Ответ 1

Как это сработает? Он принимает только пару итераторов. Итераторам не нужно сохранять ссылку на свой контейнер (векторные итераторы могут быть просто псевдонимами для указателей).

Итак, как алгоритм изменит сам контейнер? Ему нужен этот доступ.

Это должна быть функция-член.

Ответ 2

Равномерное стирание контейнера было добавлено в std/experimental int the Library Fundamentals 2 TS в конце 2014 года. В последнее время это было принято в стандарте для С++ - 2a, но даже черновик от github пока не показывает его. Я знаю, что gcc, clang и Visual Studio поддерживают экспериментальные.

Поэтому вместо обычного контейнера можно включить одну из следующих версий:

<experimental/deque>
<experimental/forward_list>
<experimental/list>
<experimental/map>
<experimental/set>
<experimental/string>
<experimental/unordered_map>
<experimental/unordered_set>
<experimental/vector>

Это подписи из более поздней работы:

  // <experimental/string>
  template <class charT, class traits, class A, class Predicate>
    void erase_if(basic_string<charT, traits, A>& c, Predicate pred);
  template <class charT, class traits, class A, class U>
    void erase(basic_string<charT, traits, A>& c, const U& value);

  // <experimental/deque>
  template <class T, class A, class Predicate>
    void erase_if(deque<T, A>& c, Predicate pred);
  template <class T, class A, class U>
    void erase(deque<T, A>& c, const U& value);

  // <experimental/vector>
  template <class T, class A, class Predicate>
    void erase_if(vector<T, A>& c, Predicate pred);
  template <class T, class A, class U>
    void erase(vector<T, A>& c, const U& value);

  // <experimental/forward_list>
  template <class T, class A, class Predicate>
    void erase_if(forward_list<T, A>& c, Predicate pred);
  template <class T, class A, class U>
    void erase(forward_list<T, A>& c, const U& value);

  // <experimental/list>
  template <class T, class A, class Predicate>
    void erase_if(list<T, A>& c, Predicate pred);
  template <class T, class A, class U>
    void erase(list<T, A>& c, const U& value);

  // <experimental/map>
  template <class K, class T, class C, class A, class Predicate>
    void erase_if(map<K, T, C, A>& c, Predicate pred);
  template <class K, class T, class C, class A, class Predicate>
    void erase_if(multimap<K, T, C, A>& c, Predicate pred);

  // <experimental/set>
  template <class K, class C, class A, class Predicate>
    void erase_if(set<K, C, A>& c, Predicate pred);
  template <class K, class C, class A, class Predicate>
    void erase_if(multiset<K, C, A>& c, Predicate pred);

  // <experimental/unordered_map>
  template <class K, class T, class H, class P, class A, class Predicate>
    void erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred);
  template <class K, class T, class H, class P, class A, class Predicate>
    void erase_if(unordered_multimap<K, T, H, P, A>& c, Predicate pred);

  // <experimental/unordered_set>
  template <class K, class H, class P, class A, class Predicate>
    void erase_if(unordered_set<K, H, P, A>& c, Predicate pred);
  template <class K, class H, class P, class A, class Predicate>
    void erase_if(unordered_multiset<K, H, P, A>& c, Predicate pred);

Ответ 3

Клей между последовательностями и алгоритмами - итераторы (спасибо @Pete Becker за то, что указали мне правильный термин здесь). Они сводятся к минимальной функциональности, необходимой для мощных алгоритмов, которые могут применяться к нескольким типам последовательностей (контейнеров). Примером может служить такой фрагмент:

std::vector<int> vec{1, 2, 3, 4};
std::list<int> lst;

std::copy(vec.cbegin(), vec.cend(), std::back_inserter(lst));
std::copy(lst.cbegin(), lst.cend(), std::ostream_iterator<int>(std::cout, " "));

Здесь std::copy может быть реализована независимо от определенного типа последовательности, в котором он работает, поскольку он работает с итераторами, которые предназначены для выполнения двух действий: перемещение последовательности и обеспечение доступа к ее элементам.

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

Необходимость сделать это при одновременном использовании алгоритма (std -) достаточно распространена, чтобы привести к тому, что идиома удаления стирания является хорошо известной библиотекой шаблонов или диапазонов, которые используют преимущества всех контейнеров, передаваемых алгоритмам:

#include <boost/range/algorithm_ext.hpp>

std::vector<int> vec{1, 2, 3, 4};

boost::remove_erase(vec, 3);

Последний вызов функции может фактически удалить элемент со значением 3 из контейнера, потому что в этом вызове функции не было скрыто никакой информации. Напротив, семейство функций *begin/*end (member) выполняет именно это: скрывает информацию за абстракцией.