【问题标题】:STL remove doesn't work as expected?STL 删除没有按预期工作?
【发布时间】:2011-06-23 15:44:47
【问题描述】:
int main()
{

        const int SIZE = 10;
        int a[SIZE] = {10, 2, 35, 5, 10, 26, 67, 2, 5, 10};
        std::ostream_iterator< int > output(cout, " ");
        std::vector< int > v(a, a + SIZE);
        std::vector< int >::iterator newLastElement;

        cout << "contents of the vector: ";
        std::copy(v.begin(), v.end(), output);

        newLastElement = std::remove(v.begin(), v.end(), 10);
        cout << "\ncontents of the vector after remove: ";
        //std::copy(v.begin(), newLastElement, output); 
                         //this gives the correct result : 2 35 5 26 67 2 5
        std::copy(v.begin(), v.end(), output);
          //this gives a 10 which was supposed to be removed : 2 35 5 26 67 2 5 2 5 10

        cout << endl;
        return 0;
}

数组a中有3个10。

为什么我们用remove函数删除所有10后数组v包含一个10。

你也可以看到编译后的输出here

【问题讨论】:

  • 顺便问一下,这条评论 (//this gives the correct result : 2 35 5 26 67 2 5 2 5) 是程序真正发出的吗?你真的是想说2 35 5 26 67 2 5,对吧?
  • 是的,完全正确。感谢指出

标签: c++ stl


【解决方案1】:

实际上std::remove 不会从容器中删除该项目。引用自here

Remove 从范围[first, last) 中删除所有等于value 的元素。也就是说,remove 返回一个迭代器new_last,使得[first, new_last) 范围内不包含等于value 的元素。 [new_last, last) 范围内的迭代器都仍可取消引用,但它们指向的元素未指定 Remove 是稳定的,这意味着不等于 value 的元素的相对顺序不变。`

也就是说,std::remove 仅与一对迭代器一起工作,并且对实际包含项目的容器一无所知。事实上,std::remove 不可能知道底层容器,因为它无法从一对迭代器中发现迭代器所属的容器。所以std::remove 并没有真正删除这些项目,只是因为它不能实际上从容器中删除项目的唯一方法是调用该容器上的成员函数。

因此,如果您想删除项目,请使用Erase-Remove Idiom

 v.erase(std::remove(v.begin(), v.end(), 10), v.end()); 

erase-remove idiom 如此常见和有用的是,std::list 添加了另一个名为 list::remove 的成员函数,它产生与 erase-remove 成语相同的效果。

 std::list<int> l;
 //...
 l.remove(10); //it "actually" removes all elements with value 10!

这意味着,当您使用 std::list 时,您不需要使用 erase-remove 成语。可以直接调用它的成员函数list::remove

【讨论】:

  • 似乎cplusplus.com 已更正所有定义,请查看并编辑您的答案,因为它对该网站产生负面影响。
【解决方案2】:

原因是 STL 算法不会修改序列的大小。 remove,不是实际擦除项目,而是移动它们并将迭代器返回到“新”端。然后可以将该迭代器传递给容器的erase 成员函数以实际执行删除:

v.erase(std::remove(v.begin(), v.end(), 10), v.end());

顺便说一句,这被称为“擦除删除成语”。

编辑:我错了。请参阅 cmets 和 Nawaz 的回答。

【讨论】:

  • Nawaz 的回答说,新结束后的元素未指定,不一定等于删除的元素,因此您看到的是特定实现的工件。重命名函数是不合适的。
  • std::remove 不是move matching values to the end of the sequence 所必需的,正如您所想的那样。
【解决方案3】:

因为std::remove 实际上并没有缩小容器,它只是将所有元素向下移动以填充“已删除”元素使用的位置。例如,如果您有一个序列1 2 3 4 5 并使用std::remove 删除值2,您的序列将看起来像1 3 4 5 5。如果您随后删除值4,您将获得1 3 5 5 5。在任何时候都不会告诉序列要更短。

【讨论】:

    猜你喜欢
    • 2015-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 2021-10-19
    • 2020-03-18
    • 2012-06-14
    • 2014-11-15
    相关资源
    最近更新 更多