STL 删除无法按预期工作?

问题描述 投票:0回答:5
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。

您也可以在这里看到编译的输出

c++ stl
5个回答
38
投票

实际上

std::remove
不会从容器中移除该物品。引自这里

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 惯用法如此常见和有用,因为

std::list
添加了另一个名为
list::remove
的成员函数,它产生与
erase-remove
惯用法相同的效果。

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

这意味着,当您使用

erase-remove
时,不需要使用
std::list
习惯用法。可以直接调用它的成员函数
list::remove


13
投票

原因是STL算法不会修改序列的大小。

remove
,不是实际删除项目,而是移动它们并将迭代器返回到“新”端。然后可以将该迭代器传递给容器的
erase
成员函数以实际执行删除:

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

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

编辑:我错了。查看评论和纳瓦兹的回答。


3
投票

C++20 引入了一个新的非成员函数

std::erase
,它简化了所有标准库容器的此任务。

此处多个旧答案建议的解决方案:

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

现在可以写成:

std::erase(v, 10);

1
投票

因为

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


0
投票

C++ 自 2018 年起就有擦除算法(免费函数)https://en.cppreference.com/w/cpp/container/vector/erase2。 这直接作用于容器:

std::erase(v, 10);

这是 C++20 中的。还有一个适用于谓词的版本:

std::erase_if(v, std::less<int>{}(10));
© www.soinside.com 2019 - 2024. All rights reserved.