2014-04-04 107 views
10

人們普遍認爲,從std::vector完全刪除所需項目的好方法是erase-remove idiomSTL「擦除 - 刪除」成語:爲什麼不「調整大小 - 刪除」?

正如上面的鏈接注意(如本發佈之日起),在代碼中擦除刪除成語是這樣的:

int main() 
{ 
    // initialises a vector that holds the numbers from 0-9. 
    std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 

    // erase-remove idiom to completely eliminate the desired items from the vector 
    v.erase(std::remove(std::begin(v), std::end(v), 5), std::end(v)); 
} 

我想知道resize-remove成語是否功能和性能等同於erase-remove習語。或者,也許我錯過了一些明顯的東西?

以下是resize-remove成語等同於上述erase-remove成語嗎?

int main() 
{ 
    std::vector<int> v = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 

    // Is this "resize-remove" approach equivalent to the "erase-remove" idiom? 
    v.resize(std::remove(std::begin(v), std::end(v), 5) - v.begin()); 
} 
+5

「resize-remove」是 - 根據定義 - 不是「成語」,因爲該短語不被其他人使用...... –

+0

@TonyD這本身就是一個非常強烈的理由,喜歡刪除成語。 –

回答

10

在我看來,有兩個原因:

  1. std::remove算法只需要前向迭代器,但是-需要隨機訪問迭代器。

  2. std::remove的結果表示「容器的新結束」。從邏輯上講,我們應該抹掉[「容器的新結束」,「容器的舊結束」)。

+3

第一個是非常好的一點(儘管它不會影響'std :: vector')。 –

1

我覺得eraseerase(first,last)保證first之前沒有單元訪問或修改,而resize只保證這個在沒有重新分配是因爲調整大小的。

編輯:爲別人指出的那樣,這樣的重新分配將永遠不會發生,所以沒有區別

+1

確保將尺寸調整爲較小尺寸不會重新分配。 –

+1

更明確地說:'resize'是用'erase'和'insert'來定義的。所以當它擦除時,它與'擦除'具有相同的保證。 –

5

它等價於std :: vector,但不適用於std :: list或其他容器。不知道是否減法迭代器甚至可以用於std :: list,即使它是,它也是一個O(N)操作。

+1

這是不可能的。你必須使用'std :: distance',它是O(N)。 –

+0

然後再次,它是O(N)中的元素刪除,再加上它將這些元素緩存。所以實際上它並不可怕。 – MSalters

+0

@ MSalters:這是相反的。元素中的O(N)不能刪除。這個成語的整體複雜性不會改變,因爲'std :: remove()'已經是O(N),但在實踐中常量也很重要。 –

2

它應該沒有任何區別; resizeinserterase的條款 定義。但通常最好使用標準成語 ,以便於識別。而 當然,刪除刪除語言將與任何序列 容器,而不僅僅是那些支持resize。 (所有的 標準集裝箱似乎支持resize,但 似乎並沒有成爲一個要求。因此,它可能無法使用 用戶定義的容器,即使他們支持所有 所需的操作。)

在性能方面:resize必須做一個額外的 測試,以確定它是擦除還是插入,但我無法想象這會產生重大影響。

+0

'std :: list'上的'resize'還有一個缺點,就是它必須通過迭代O(n)來找到列表的新結尾,而'remove'返回的精確結束迭代器已經被拋棄了計算'X-begin(),這又是O(n)。 –

+0

@ArneMertz:'std :: list'是雙向的(雙鏈接)。找到新的結果非常便宜:您只需彈出當前的結尾,直到尺寸合適。新的結尾是最後一個彈出元素的前身。 – MSalters

+0

@ArneMertz我在想這件事。我沒想到甚至找不到它,因爲它不能有效地實施。 (當然,如果它們不是隨機訪問,你不能直接對迭代器做不同的處理) –

相關問題