2016-04-17 66 views
4

標準庫容器允許我們使用迭代器firstlast表示的erase範圍。在標準或設計決策中是否擦除了最終迭代器?

std::vector<foo> bar; 
//   first it  last it 
bar.erase(bar.begin(), bar.end()); 

的標準指出,first迭代器必須是有效的提領,而last只需要有效。但是,如果first == last,則first不需要可解引用,因爲erase是無操作的。這意味着以下是合法的:

bar.erase(bar.end(), bar.end()); 

但是如果我只是想刪除一個元素,而不是一個範圍的迭代器必須是有效的提領做出如下未定義行爲:

bar.erase(bar.end()); 

爲什麼這不是一個沒有操作?這是標準委員會的疏忽,將在未來的語言版本中加以處理,還是我沒有看到這一點的故意設計決定?

至於我可以看到它在執行類似下面的時候沒有提供好處,但同時帶來額外的麻煩:

bar.erase(std::find(bar.begin(), bar.end(), thing)); 
+4

它將不得不比較迭代器,所以每個沒有傳入結束迭代器的人現在都會執行額外的無用檢查。無論如何,需要比較兩者的範圍以知道何時停止擦除。 – chris

+1

@chris:是的,它應該是_answer_。 –

+0

@LightnessRacesinOrbit,這實在是一種猜測。這是一個小檢查。 – chris

回答

3

C++有留下任何額外的工作出其標準庫的習慣,也被稱爲「你不支付你不使用的東西」。在這種情況下,單個迭代器版本需要將迭代器與最終迭代器進行比較。對於大多數情況下,這種檢查是多餘的,這是額外的工作,沒有被使用,儘管是一小部分工作。

erase(iterator it): 
    if it != end: // we can't avoid this check now 
     // erase element 

在超載取得範圍的情況下,它在結束之前自然停止。一個天真的實現可能如下:

erase(iterator first, iterator last): 
    while first != last: 
     erase(first++); 

它需要知道什麼時候停止刪除一些方式。在某些情況下,它可能比較聰明,比如一塊內存覆蓋已擦除的內存而沒有分支,但這隻會發生在特定的場景中。


還要注意的是,它更容易構建選中的版本從這個不是周圍的其他方法:

checked_erase(Container cont, iterator it): 
    if it != cont.end(): 
     cont.erase(it); 
+0

我已經接受了答案,因爲這是行爲的最可能原因,並且沒有其他答案似乎即將出現。然而,我很難想象一種情況,我將會處理一個迭代器,並確定它不是沒有檢查的end()。 – Fibbles

+0

@Fibbles,你清除你知道不是空的容器中的最大元素。或者第一個元素。也許你有一個庫,它返回'可選'而不是結束,你做(簡化操作)'opt.do_if_present(它 - > cont.erase(it));'當設計庫時,通常很難預見用例也有多廣泛。說明你無法想到的用途通常是很好的,但有人會不可避免地想到。 – chris

1

爲什麼不是這只是一個空操作?

爲什麼它是一個沒有操作?您正在調用專門設計用於刪除單個元素的方法,並以無法刪除任何元素的方式調用它。這沒有任何意義。你可能會爭辯說它應該拋出一個異常,而不是有未定義的行爲,但是在你的問題中,我沒有看到任何有效的論點,因爲它把它定義爲無所事事,所以我沒有理由認爲它可能是一個疏忽。

您的示例並不令人信服:只要項目在向量中是唯一的,就可以使用std::remove而非std::find以簡單形式輕鬆表達。如果該項目不是唯一的,則更明確地指出要刪除多少項目。

從另一個角度看:erase(it)相當於所有迭代器的erase(it, it+1)。包括結束迭代器。爲erase(it)定義了一個特殊的例外,其中erase(it, it+1)未定義的末端迭代器將引入不一致性。

+0

考慮一個元素可能是唯一的或不存在的向量。使用std :: remove即使在找到唯一元素後也會不必要地搜索整個向量,而std :: find只要找到元素就會停止。當然,正確的方法可能會像std :: unique與擦除一樣。 std :: find例子只是我想到的第一件事情。 – Fibbles

+0

@Fibbles你也可以使用帶有謂詞的'remove_if',一旦找到匹配就停止比較。 – hvd