2009-12-05 11 views
9

Niedawno dowiedziałem się o właściwym sposobie pracy z odwrotnymi iteratorami w C++ (szczególnie, gdy trzeba je usunąć). (Patrz this question i this one.)Używaj zwykłego iteratora do iteracji wstecznej lub zmagań z reverse_iterator?

ten sposób jesteś ma to zrobić:

typedef std::vector<int> IV; 
for (IV::reverse_iterator rit = iv.rbegin(), rend = iv.rend(); 
    rit != rend; ++rit) 
{ 
    // Use 'rit' if a reverse_iterator is good enough, e.g., 
    *rit += 10; 
    // Use (rit + 1).base() if you need a regular iterator e.g., 
    iv.erase((rit + 1).base()); 
} 

Ale myślę , że to jest o wiele lepsza (nie rób tego, a nie normy Wymaganie jest spełnione, jak MooingDuck zwraca uwagę)

for (IV::iterator it = iv.end(), begin = iv.begin(); 
    it-- != begin;) 
{ 
    // Use 'it' for anything you want 
    *it += 10; 
    iv.erase(it); 
} 

Wady:

  • Powiedz mi. Co jest nie tak z tym?
  • Nie jest zgodny ze standardami, jak wskazuje MooingDuck. To prawie unieważnia każdą z możliwych zalet poniżej.

Plusy:

  • Wykorzystuje znajome idiom na odwrocie for-pętle
  • Nie trzeba pamiętać (lub wyjaśnienie) w +1
  • mniej pisania
  • Works dla std : lista też: it = il.erase(it);
  • Po usunięciu elementu nie trzeba dostosowywać iteratora
  • Jeśli wymazać, nie trzeba ponownie obliczyć iterator rozpocząć
+0

Masz na myśli, oprócz tego, że jest to niezdefiniowane zachowanie i zawiedzie/zawiesi się w typowych sytuacjach? Wypróbuj za pomocą pustej 'mapy'. –

+0

dbać o opracowanie w odpowiedzi? Czy UB dekrementuje iterator wejściowy lub dekrementujący poza początkiem? Czy to UB dla wszystkich pojemników? – Dan

+0

Nie można zmniejszyć wartości iteratora wejściowego lub wyjściowego (zapomniałem o tym, dobre oko), a także nie można zmniejszyć wartości początkowej dla żadnego kontenera. –

Odpowiedz

7

Powód odwrotnych iteratorów polega na tym, że standard algorithms nie wie, jak przerobić kolekcję wstecz. Na przykład:

#include <string> 
#include <algorithm> 
std::wstring foo(L"This is a test, with two letter a's involved."); 
std::find(foo.begin(), foo.end(), L'a'); // Returns an iterator pointing 
             // to the first a character. 
std::find(foo.rbegin(), foo.rend(), L'a').base()-1; //Returns an iterator 
               // pointing to the last A. 
std::find(foo.end(), foo.begin(), L'a'); //WRONG!! (Buffer overrun) 

Użycie któregokolwiek z iteratorów daje w wyniku jaśniejszy kod.

+0

Dobrze, że mogą istnieć pewne generyczne alg, które będą działać na reverse_iterators i może nie być ' odwrócona "wersja tego pliku do użytku w zwykłych iteratorach. Do ściągnięcia możesz użyć find_last_of, ale jeśli był to jakiś inny rodzaj kontenera, który nie jest opcją. – Dan

+0

BTW twoje drugie wywołanie std :: find() zwraca iterator wskazujący na '\' '(apostrof). To wskazuje na 'a': std :: wstring :: iterator iter = (std :: find (foo.rbegin(), foo.rend(), 'a') + 1) .base(); – Dan

+0

Dobra uwaga, naprawiona :) –

3

Na co warto, Scott Meyers' Efektywna STL zaleca, aby po prostu trzymać się regularne ol”iterator (pkt 26).

+2

Mówi się również, aby unikać jawnych pętli, a 'reverse_iterator' jest czasami niezbędny do osiągnięcia tego. Punkt 26 mówi tylko o pętlach jawnych. –

+0

Co więcej, oznacza to, że kod OP jest w porządku, kiedy tak naprawdę jest Niezdefiniowane zachowanie (i zawiedzie w typowych sytuacjach) –

Powiązane problemy