代码之家  ›  专栏  ›  技术社区  ›  Gustavo Muenz

vector::erase和std::remove\u的异常行为,如果结束范围与vector.end()不同

  •  7
  • Gustavo Muenz  · 技术社区  · 16 年前

    我需要从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());
    

    在这之后,我希望 ints 向量有:[1,2,3,5,6]。

    std::remove_if 线条,元素 ints公司 被修改了,我猜我进入了某种未定义的行为。

    那么,如何从一个向量的范围中移除元素呢?

    3 回复  |  直到 16 年前
        1
  •  13
  •   Tyler McHenry    16 年前

    编辑:

    remove_if 是:

    1  2  3  4  5  6
          ^     ^
        begin  end
    

    以及 算法会查看 begin 和 end (包括 开始 ,但不包括 ),并删除与谓词匹配的所有元素。那以后呢 删除\u if 你的向量是这样的

    1  2  3  ?  5  6
          ^  ^
      begin  new_end
    

    在哪里? ? 是一个我不认为是确定的值,尽管如果它被保证是任何东西,它将是 4 new_end ,此时将删除匹配的元素 std::remove_if . 请注意 std::删除 除了你给它的子序列之外,什么都不要碰。用一个更广泛的例子来说,这可能更有意义。

    1  2  3  4  5  6  7  8  9  10
          ^              ^
        begin           end
    

    之后 std::删除 ,您将得到:

    1  2  3  5  7  ?  ?  8  9  10
          ^        ^
        begin      new_end
    

    想想这个。它所做的是将4和6从子序列中移除,然后移动所有内容 在子序列内 向下填充删除的元素,然后移动 结束 , ]它产生的序列与( 开始 , 结束 ]您传入的子序列,但删除了某些元素。任何超过或等于 你过去的那件事没有受到影响。

    那么,你想摆脱的是 . 这些是 “垃圾”值。所以你的擦除调用应该是:

    ints.erase(it, ints.begin()+4);
    

    呼叫 erase

    让事情变得复杂的是 erase() 在向量上,或在任意点更改向量的大小。它只是移动元素,并在您要求它处理的子序列结束后留下一些“垃圾”元素。这看起来很愚蠢,但是STL这样做的全部原因是为了避免doublep带来的无效迭代器的问题(并且能够在不是STL容器的东西上运行,比如原始数组)。

        2
  •  1
  •   Nathan Ernst    16 年前

    std::remove_if 将它“移除”的元素移动到输入范围的末尾。在这种情况下,您需要做的是:

    ints.erase(it, ints.begin() + 4 /* your end of range */);
    

    从C++中简单地说:

    “删除”pred返回的项目 范围[first,last]为false。 范围结束。相对顺序 未删除的项的数目为

    底层容器;取而代之的是 右边的是新的 位置,以便覆盖 pred返回false的元素。 例如移除过程。

        3
  •  1
  •   doublep    16 年前

    std::vector 使迭代器超过删除的元素无效,因此不能使用接受范围的“外部”函数。你需要用另一种方式。

    编辑:

    一般来说,您可以使用这样一个事实,即删除一个元素会将位于其他位置的所有元素“向后移动”一次。像这样:

    for (size_t scan = 2, end = 4; scan != end; )
      {
         if (/* some predicate on ints[scan] */)
           {
             ints.erase (ints.begin () + scan);
             --end;
           }
         else
           ++scan;
      }
    

    标准::向量 不适合在中间擦除元素。你应该考虑别的事情。 std::list ?)如果你经常这样做。

    编辑2:

    正如评论所阐明的,第一段是不正确的。在这种情况下 std::remove_if