我正在阅读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 (???)
答案 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”将解决尾部僵尸的问题。