![](/img/trans.png)
[英]std::remove_if and erase not removing elements from std::vector
[英]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
算法查看begin
和end
之間的所有數字(包括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
迭代器移動到同一子序列的新結尾。 目標是滿足以下要求:它生成的( begin
, new_end
)序列與您傳入的( begin
, end
)子序列相同,但刪除了某些元素。在傳入的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.