在擦除习语之外使用“remove”有什么原因吗?

Is there a reason to use `remove` outside of the erase-remove idiom?

本文关键字:什么 remove 习语 擦除      更新时间:2023-10-16

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

  1. 将所有不需要删除的元素移动到数组的前面。
  2. 将数组标记为更小

这可以在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 s替换为B s,并要求所有新的B s将是连续的。这没什么意义。

edit:它对连续内存容器(dequevector)以外的任何东西有意义吗?

如果我确实是正确的,为什么它被实现为一个独立的算法,而不是vector::remove_if, dequeue::remove_if

你的问题问反了。相反,人们会问"为什么我要为每个容器重复实现相同的算法,而不是使其成为一个单一的自由函数"?

将容器、迭代器和算法分开的关键思想是,当你编写更多的算法和容器时,复杂性不会爆炸。如果你想编写一个具有连续存储的新容器,你可以在它上面直接使用remove(如果你提供了前向或随机访问迭代器),而不需要复制任何代码。

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

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

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

值得注意的是,remove规范中"left out"的元素具有未指定的值,这使得新的c++ 11 move操作可以应用于有效的移除。

现在,我承认大多数时候,它是作为remove-erase习语的一部分使用的。没有人能阻止你创建自己的:
 template <typename C>
 void erase(C& container, typename C::const_reference r) {
   container.erase(std::remove(container.begin(), container.end(), r), container.end());
 }

是。简单的反例:

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向量。当vector超出作用域时,所有元素都会被析构。

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

据我所知,被删除的元素的值在删除后是未指定的,所以你不能使用它们的数据。它们被移除了,但是空间没有被释放。

是。首先,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,它使它更小,从而执行内存重新分配。这并不总是理想的,因为这会导致vector的其他元素的大量复制构造函数