为什么没有std :: erase?

19

在阅读STL时,我意识到它没有提供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());

尽管如此,它嵌入到每个容器中。如果性能是原因,在对象连续的情况下,可以一次性删除这些对象。但我猜使用模板特化可以实现这一点。


我认为,如果答案是“可以实现但C++决定不这样做”,那么这个问题将被关闭为基于观点的。 - user202729
注意:Java确实提供了通过迭代器从集合中删除项目的功能;但是,许多集合是不可变的,因此这个操作并不总是有效,导致API不一致。 - Reinstate Monica
3个回答

30

它将如何工作?它只接受一对迭代器。迭代器不必保留对其容器的引用(向量的迭代器可以是指针的简单别名)。

那么,该算法如何修改容器本身?它需要访问权限。

这必须是成员函数。


12

统一容器擦除在2014年底被添加到std / experimental库基础知识2 TS中。最近为C ++ -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);

它是C++17标准的一部分吗? - Amit Bhaira
很遗憾,我认为它仍然在std/experimental中。我会编辑我的帖子。 - emsr
是的,也许这就是它未在此处列出的原因 - https://en.cppreference.com/w/cpp/algorithm - Amit Bhaira
1
如果p0251r0通过了,那么这个提案和更多内容将会是多余的。可惜。但是,不管怎样,加一。 - StoryTeller - Unslander Monica
2
这个功能被添加到了C++20中,并且已经在gcc-9、clang-7和最近的Visual Studio中实现。 - emsr

7
序列和算法之间的黏合剂是迭代器(感谢 @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(成员)函数族正是这样的:它们将信息隐藏在抽象之后。


4
抽象化程度比那要更抽象一些。迭代器是序列和算法之间的粘合剂。容器是管理序列的一种方式,但它们并不是唯一的方式。例如,您可以创建一对迭代器来遍历输入流;很少有人会称输入流为容器。无论如何加1。 - Pete Becker
@PeteBecker 谢谢你的提示!我改进了答案。 - lubgr
据我所知,有一个提案要添加(重载集)std::erase_if,类似于boost::remove_erase - Caleth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接