vector :: erase和std :: remove_if的奇怪行为,其结束范围与vector.end()不同

Edi*_*enz 7 c++ stl

我需要从std :: vector的中间删除元素.

所以我尝试过:

struct IsEven {
    bool operator()(int ele)
    {
        return ele % 2 == 0;
    }
};

    int elements[] = {1, 2, 3, 4, 5, 6};
    std::vector<int> ints(elements, elements+6);

    std::vector<int>::iterator it = std::remove_if(ints.begin() + 2, ints.begin() + 4, IsEven());
    ints.erase(it, ints.end());
Run Code Online (Sandbox Code Playgroud)

在此之后我会期望ints向量具有:[1,2,3,5,6].

在Visual Studio 2008的调试器中,std::remove_if在行之后,元素ints被修改,我猜我在这里遇到某种未定义的行为.

那么,如何从矢量范围中删除元素?

Tyl*_*nry 13

编辑:对不起,原始版本不正确.固定.

这是正在发生的事情.您的输入remove_if是:

1  2  3  4  5  6
      ^     ^
    begin  end
Run Code Online (Sandbox Code Playgroud)

remove_if算法查看begin和之间的所有数字end(包括begin但不包括end),并删除与谓词匹配的所有元素.所以在remove_if运行之后,你的矢量看起来像这样

1  2  3  ?  5  6
      ^  ^
  begin  new_end
Run Code Online (Sandbox Code Playgroud)

?我不认为的价值在哪里是确定性的,尽管如果它保证是任何它将是什么4.并且new_end,指向您给出的输入序列的新结束,现在删除匹配元素,返回的是std::remove_if.请注意,std::remove_if除了您提供的子序列之外,不会触及任何内容.对于更广泛的示例,这可能更有意义.

说这是你的输入:

1  2  3  4  5  6  7  8  9  10
      ^              ^
    begin           end
Run Code Online (Sandbox Code Playgroud)

之后std::remove_if,你得到:

1  2  3  5  7  ?  ?  8  9  10
      ^        ^
    begin      new_end
Run Code Online (Sandbox Code Playgroud)

对此稍加思考.它所做的是从子序列中删除4和6,然后将子序列中的所有内容向下移动以填充已删除的元素,然后将end迭代器移动到同一子序列的新结尾.目标是满足这样的要求,即它产生的(,]序列与你传入的(begin,new_end)子序列相同,但是删除了某些元素.传入的任何内容都不会受到影响.beginendend

那么,你想要摆脱的是返回的结束迭代器和你给它的原始结束迭代器之间的所有东西.这些是?"垃圾"值.所以你的擦除电话实际应该是:

ints.erase(it, ints.begin()+4);
Run Code Online (Sandbox Code Playgroud)

调用erase你刚刚删除了你执行删除的子序列结束之后的所有内容,这不是你想要的.

使这种复杂化的原因是remove_if算法实际上不会调用erase()向量,或者在任何点改变向量的大小.它只是移动元素并在您要求它处理的子序列结束后留下一些"垃圾"元素.这看起来很愚蠢,但是STL这样做的全部原因是避免了doublep带来的无效迭代器的问题(并且能够运行不是STL容器的东西,比如原始数组).