2010-12-18 49 views
36

好的,我希望我在這裏犯了一個愚蠢的錯誤。我有一個DisplayDevice3d列表,每個DisplayDevice3d包含一個DisplayMode3d列表。我想從DisplayDevice3d列表中刪除沒有任何DisplayMode3d的所有項目。我試圖使用lambda做到這一點,即:std :: remove_if - lambda,不會從集合中刪除任何東西

// If the device doesn't have any modes, remove it. 

    std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(), 
    [](DisplayDevice3d& device) 
    { 
    return device.Modes.size() == 0; 
    } 
); 

即使出6名DisplayMode3d在MyDisplayDevices,只有1個有其模式集合中的任何DisplayMode3d的,沒有什麼是被從列表中刪除。

我在這裏犯了什麼數字錯誤?

編輯:

啊好吧,我錯了,我應該使用MyDisplayDevices.remove_if代替的std ::的remove_if的,但是下面的答案是使用std ::的remove_if正確的:P。

MyDisplayDevices.remove_if([](DisplayDevice3d const & device) 
          { 
           return device.Modes.size() == 0; 
          }); 
+3

如果容器本身支持remove_if,那麼一定要用它。我相信這是std :: list的情況。對於不提供remove_if的容器,可以將std :: remove_if與容器的擦除成員函數結合使用。 – sellibitze 2010-12-18 18:39:05

+0

@sellibitze換句話說,老鼠毒 – bobobobo 2013-05-04 01:30:03

+0

可能的重複[Erasing elements from a vector](http://stackoverflow.com/questions/347441/erasing-elements-from-a-vector) – bobobobo 2013-05-04 01:36:52

回答

63

你需要調用擦除的迭代器的remove_if返回,它應該是這個樣子:

auto new_end = std::remove_if(MyDisplayDevices.begin(), MyDisplayDevices.end(), 
           [](const DisplayDevice3d& device) 
           { return device.Modes.size() == 0; }); 

MyDisplayDevices.erase(new_end, MyDisplayDevices.end()); 
+0

工程很棒。謝謝。 – Robinson 2010-12-18 15:33:56

+0

[另一個需要](http://stackoverflow.com/a/10360617/111307) – bobobobo 2013-05-04 01:35:17

17

remove_if不會從列表中刪除任何東西只是將它們移動到結束。您需要將其與erase一起使用。有關更多詳細信息,請參閱此question

+1

所以我從迭代器中擦除它返回到列表的末尾? – Robinson 2010-12-18 15:27:50

+0

@Robinson:是的。 – Asha 2010-12-18 15:30:39

+4

「只是將它們移動到最後」並不完全正確。 – sellibitze 2010-12-18 18:35:53

2

正如其他人所說,有辦法使它工作。不過,我的建議是完全避免remove_if,並堅持標準的基於迭代器的刪除。下面的成語既適用於list也適用於vector,並且不會產生意外的行爲。

for(vector<TYPE>::iterator iter = vec.begin() ; iter != vec.end() ;) 
    if(iter->shouldRemove) 
    iter = vec.erase(iter) ; // advances iter 
    else 
    ++iter ; // don't remove 

如下面提及的註釋,這種方法確實有更高的成本比remove_if當多於1個元素被去除。

remove_if通過複製向量中更遠前進的元素,覆蓋應該從前面的向量中刪除的向量。例如:的remove_if呼籲向量刪除所有0元素:

0 1 1 0 1 0 

結果:

1 1 1 0 1 0 

注意向量是多麼的不正確呢。這是因爲remove_if將迭代器返回到最後一個有效元素......它不會自動調整向量的大小。您仍然需要撥打v.erase()上的電話號碼remove_if返回的迭代器。

下面是一個例子

#include <stdio.h> 
#include <vector> 
#include <algorithm> 
#include <functional> 
using namespace std; 

void print(vector<int> &v) 
{ 
    for(int i : v) 
    printf("%d ", i); 
    puts(""); 
} 

int main() 
{ 
    vector<int> v = { 0, 1, 1, 0, 1, 0 }; 
    print(v); // 0 1 1 0 1 0 
    vector<int>::iterator it = remove_if(v.begin(), v.end(), [](int i){ return i == 0; }); 
    print(v); // 1 1 1 0 1 0 
    v.erase(it, v.end()); // actually cut out values not wanted in vector 
    print(v); // 1 1 1 (correct) 
} 
+3

爲什麼你會推薦這個方法通過* remove_if()*?當然,* remove_if()*不會產生「意外的行爲」(它只是名字很差= P)。而std :: remove_if()將爲編譯器提供更多智能優化的機會,不是嗎?因爲它從頭到尾迭代,而*保證編譯器不會發生有趣的事情,而不像手動迭代。 (即相同的優化*範圍-for()*超過了常規*()*) – 2015-02-27 05:02:51

+5

bobobobo:問題是你的算法對於向量很慢。每個「擦除」都會將剩餘的元素向下移動一個。如果你正在清除1000個元素中的50個元素,那就是~50000個移動,你只需要將大約1000個倖存者移動到他們的最終位置。 – 2015-03-10 15:32:41

5

remove_if不執行調整大小,而是它僅返回迭代後面沒有去除的最後一個元素的元素。這個迭代器可以傳遞給erase()進行清理。

enter image description here