簡體   English   中英

vector :: erase和std :: remove_if的奇怪行為,其結束范圍與vector.end()不同

[英]Weird behaviour with vector::erase and std::remove_if with end range different from vector.end()

我需要從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向量具有:[ ints ]。

在Visual Studio 2008的調試器中,在std::remove_if行之后, ints的元素被修改,我猜我在這里遇到某種未定義的行為。

那么,如何從矢量范圍中刪除元素?

編輯:對不起,原始版本不正確。 固定。

這是正在發生的事情。 您對remove_if輸入是:

1  2  3  4  5  6
      ^     ^
    begin  end

remove_if算法查看beginend之間的所有數字(包括begin ,但不包括end ),並刪除與謂詞匹配的所有元素。 所以在remove_if運行之后,你的矢量看起來像這樣

1  2  3  ?  5  6
      ^  ^
  begin  new_end

哪里? 是一個我認為不具有確定性的價值,但如果它保證是任何它將是4 new_end指向你給它的輸入序列的新結尾現在刪除匹配元素,是std::remove_if返回的內容。 請注意, std::remove_if不會觸及超出您提供的子序列的任何內容。 對於更廣泛的示例,這可能更有意義。

說這是你的輸入:

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

std::remove_if ,你得到:

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

對此稍加思考。 它所做的是從子序列中刪除4和6,然后將子序列中的所有內容向下移動以填充已刪除的元素,然后將end迭代器移動到同一子序列的新結尾。 目標是滿足以下要求:它生成的( beginnew_end )序列與您傳入的( beginend )子序列相同,但刪除了某些元素。在傳入的end或之外的任何內容沒有動過。

那么,你想要擺脫的是返回的結束迭代器和你給它的原始結束迭代器之間的所有東西 這些是? “垃圾”的價值觀。 所以你的擦除電話實際應該是:

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

erase的調用你剛剛刪除了你執行刪除的子序列末尾之外的所有內容,這不是你想要的。

使這復雜化的原因是remove_if算法實際上不會對向量調用erase() ,或者在任何點改變向量的大小。 它只是移動元素並在您要求它處理的子序列結束后留下一些“垃圾”元素。 這看起來很愚蠢,但是STL這樣做的全部原因是避免了doublep帶來的無效迭代器的問題(並且能夠運行不是STL容器的東西,比如原始數組)。

刪除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::vector不適合在中間擦除元素。 如果你經常這樣做,你應該考慮別的東西( std::list ?)。

編輯2:

正如評論所闡明的那樣,第一段並非如此。 在這種情況下, std::remove_if應該比我在第一次編輯中建議的更有效,所以忽略這個答案。 (保留評論。)

這種行為並不奇怪 - 你正在消除錯誤的范圍。 std::remove_if將它“移除”的元素移動到輸入范圍的末尾。 在這種情況下,您正在尋找的是:

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

從Cuts到Nutshell:

remove_if函數模板“刪除”pred從[first,last]范圍返回false的項目。 返回值是范圍新結束之后的一個值。 未刪除的項目的相對順序是穩定的。

實際上沒有任何東西從底層容器中刪除; 相反,右邊的項目被分配到新的位置,因此它們會覆蓋pred返回false的元素。 有關刪除過程的示例,請參見圖13-13(在remove_copy下)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM