不理解C ++ STL中std :: remove的结果

时间:2013-09-26 20:32:14

标签: c++ c++11 stl

我正在阅读Josuttis“The C ++ Standard Library,2nd ed。”。在第6.7.1节中,作者解释说,下面给出的代码会给出意想不到的结果。我仍然不知道std::remove()如何运作,以及为什么我得到这个奇怪的结果。 (虽然我知道你需要使用std::erase()才能真正删除元素,实际上最好使用list::erase()而不是std::remove()&`std :: remove的组合( ))。

list<int> coll;
// insert elements from 6 to 1 and 1 to 6
for (int i=1; i<=6; ++i) {
     coll.push_front(i);
     coll.push_back(i);
}

// print
copy (coll.cbegin(), coll.cend(), // source
      ostream_iterator<int>(cout," ")); // destination
      cout << endl;

// remove all elements with value 3
remove (coll.begin(), coll.end(), // range
        3); // value
// print (same as above)   

,结果是

pre:  6 5 4 3 2 1 1 2 3 4 5 6
post: 6 5 4 2 1 1 2 4 5 6 5 6 (???) 

4 个答案:

答案 0 :(得分:4)

explanation应该有所帮助:

  

通过以这种方式移动范围内的元素来完成移除   要擦除的元素被覆盖。相对的顺序   保留的元素和物理大小   容器没有变化。迭代器指向之间的元素   新的逻辑结束和范围的物理结束仍然存在   可解除引用,但元素本身具有未指定的值。   删除调用之后通常会调用容器   擦除方法,擦除未指定的值并减少   容器的物理大小以匹配其新的逻辑大小。

请注意,std::remove()的返回值是表示新 end 的迭代器。因此,在这个新端和旧端调用std::erase()将释放你的多余空间。

答案 1 :(得分:4)

std::remove实际上并没有缩短列表。它不能 - 因为它只获取迭代器而不是容器本身。

它的作用是复制剩余的值,以便将它们放在容器的开头。但容器的最终元素(在你的情况下 - 最后两个:'5'和'6')实际上仍然存在..

使用std::remove后, 要自行缩短容器以删除剩余的“垃圾”副本。

答案 2 :(得分:2)

引用everyone's favorite c++ website

  

该函数不能改变包含该对象的对象的属性   元素范围(即,它不能改变数组的大小或   容器):通过替换比较的元素来完成删除   等于val的下一个元素没有,并发出新的信号   通过将迭代器返回到元素来缩短范围的大小   应该被视为新的过去的元素。

所以std::remove不会改变列表的大小。它删除匹配的元素并返回一个代表列表新端的迭代器。要实际删除无关元素,您需要执行以下操作:

auto it = remove(coll.begin(), coll.end(), 3);
coll.erase(it, coll.end());

答案 3 :(得分:2)

您要求算法删除“3”元素。因此,在枚举容器时,如果从中间移除某些内容,算法会移动内容。在你的情况下,这种转变发生了2次,这就是你在最后看到“5 6”元素的原因(因为实际结束被移动到前面的2个项目)。然后,“std :: erase”将解决尾部僵尸的问题。