是否有理由在擦除删除习语之外使用`remove`?

时间:2011-10-06 13:38:21

标签: c++ stl

就算法而言,从连续数组中删除一组元素可以分两部分有效地完成。

  1. 将所有不要删除的元素移动到数组的前面。
  2. 将数组标记得更小。
  3. 这可以使用erase-remove惯用法在C++中完成。

    vector<T> v; // v = {0,1,2,3,0,0,7};
    vector<T>::iterator it = remove(v.begin(),v.end(),e);
    // move all elements not to be deleted to the front
    // Yes, remove is not the brightest name for that.
    // Especially as list::remove really remove elements from the list.
    // now v = {1,2,3,7,?,?,?} results marked in question marks
    // are implementation dependent.
    v.erase(it,v.end());
    // get rid of the elements marked as question marks.
    // v = {1,2,3,7}
    

    现在,问号中元素的内容未知。我们唯一可以做的就是摆脱它们(通过覆盖它们,或者删除它们)。

    是否存在真实情况,您需要使用remove而不删除?我能想到的唯一情况是

    copy(src.begin(),src.end(),remove(v.begin(),v.end(),e),v.end());
    

    将所有A替换为B s,并要求所有这些新B都是连续的。没有多大意义。

    编辑:除了有名的内存容器(实际上是dequevector)之外的其他内容是否有意义?

    如果确实我是正确的,为什么它是作为独立算法实现的,而不是vector::remove_ifdequeue::remove_if等。

5 个答案:

答案 0 :(得分:6)

你的问题是错误的。相反,人们会问“我为什么要为每个容器重复实现这个相同的算法,而不是让它成为一个单独的自由函数”?

分离容器,迭代器和算法背后的关键思想是,在编写更多算法和更多容器时,不会出现复杂性爆炸。如果要编写具有连续存储的新容器,可以立即使用remove(如果提供转发或随机访问迭代器),而不重复任何代码。

某些容器实现自己的算法成员版本的唯一原因是因为他们可以比通用版本做得更好(例如std::set::findstd::find;或list::remove ),或者因为他们可以做通用版本不能做的事情(例如std::list::sortstd::sort)。但是如果可以的话,你应该使用免费版本来获得最大的通用性和通用性。

答案 1 :(得分:4)

首先:实际实现remove_if作为成员方法意味着一次又一次地重复代码。我一直认为这个错误出现在list中(为什么会有remove:MarkB回答这个问题,在C ++ 03中它更有效率,在C ++ 11中它的效率稍高一些)

这是STL的设计目标:单独的数据结构和算法,这样如果你有N结构和M算法,你就没有N*M算法的实现,但只有N+M,这肯定更易于管理。

值得注意的是,remove的元素“遗漏”的规范具有未指定的值,允许应用新的C ++ 11 move操作进行有效删除。

现在,我承认大多数时候,它都被用作删除 - 删除习语的一部分。没有人阻止你创建自己的:

 template <typename C>
 void erase(C& container, typename C::const_reference r) {
   container.erase(std::remove(container.begin(), container.end(), r), container.end());
 }

答案 2 :(得分:1)

是。一个简单的反例:

void bar {
  // Get vector of T's from foo()
  vector<T> v = foo();
  // Print only non-zero elements.
  vector<T>::iterator it = remove(v.begin(),v.end(), T(0));
  // I could call erase here, but why bother?
  std::copy(v.begin(), it, std::ostream_iterator<T>(std::cout));
}

如果我只使用向量的选定部分,并且没有其他用途,我不需要erase向量。当向量超出范围时,无论如何都会破坏所有元素。

答案 3 :(得分:0)

如果由于某种原因,您不想重新调整容器大小,只是删除一些元素,然后设置一些其他值来替换删除的空间,那么您可以使用remove。

据我所知,删除后删除元素的值未指定,因此您无法使用其数据。它们有点被删除,但空间没有被释放。

答案 4 :(得分:0)

是。首先,remove()与容器无关。您可以在任何容器的2个迭代器上使用它,不应该执行以下操作:

if(<iterator is vector iterator>)
{
   vector::remove_if(...);
}
else(<iterator is dequeue iterator>)
{
   dequeue::remove_if(...);
}
else(<iterator is list iterator>)
{
   list::remove_if(...);
}
// etc

是的,有理由不执行erase()。对于矢量,它使它变小,从而执行内存重新分配。什么并不总是需要,因为这将导致很多复制构造函数用于vector的其他元素。