Ters yineleyici ile silme nasıl çağrılır


181

Ben böyle bir şey yapmaya çalışıyorum:

for ( std::list< Cursor::Enum >::reverse_iterator i = m_CursorStack.rbegin(); i != m_CursorStack.rend(); ++i )
{
    if ( *i == pCursor )
    {
        m_CursorStack.erase( i );
        break;
    }
}

Ancak silme ters yineleyici değil yineleyici alır. bir ters yineleyiciyi normal bir yineleyiciye dönüştürmenin veya bu öğeyi listeden kaldırmanın başka bir yolu var mı?


17
Bir kenara, böyle döngüler yazarken, son yineleyiciyi burada yaptığınız gibi tekrar tekrar hesaplamayın i != m_CursorStack.rend(). Bunun yerine yazın i = m_CursorStack.rbegin(), end = m_CursorStack.rend(); i != end;. Yani, tekrarlanan karşılaştırma için etrafında tutabileceğiniz bir yineleyici başlatın - son konumun döngü gövdesinin bir yan etkisi olarak değişmeyeceğini varsayarsak.
seh

Bana öyle geliyor ki, buradaki en açık soru bunu neden yaptığınızdır. Listeyi tersine çevirerek ne kazanıyorsunuz? Bu kodu kullanmak yerine kendiniz yazarak ne kazanıyorsunuz std::remove?
Jerry Coffin

Ve std :: listesindeki bir yineleyici, atıfta bulunduğu öğe silindikten sonra artış için hala geçerli mi?
Steve Jessop

3
Ben sadece 1 eleman kaldırmak istiyorum, böylece 'break;', 'remove' kullanarak daha uzun süren ve istediğimi yapmadan eşleşen herhangi bir kurtulmak olacaktır. Bu özel durumda kaldırmak istediğim öğe neredeyse her zaman listenin sonu olacak veya listeye çok yakın olacak, bu nedenle tersine yineleme de daha hızlı ve soruna daha uygun.
0xC0DEFACE

4
stackoverflow.com/a/2160581/12386 Bu, tasarımcıların uygulamayı özellikle tanımlamadığını söylüyor, çünkü kullanıcı bilmeniz veya bakım yapmanız gerekmiyor, ancak yukarıdaki yukarıdaki #seh, sihirli bir şekilde rend () hesaplandığını bilmemizi bekliyor ve pahalı.
stu

Yanıtlar:


181

Biraz daha araştırma ve test yaptıktan sonra çözümü buldum. Görünüşe göre [24.4.1 / 1] standardına göre i.base () ve i arasındaki ilişki:

&*(reverse_iterator(i)) == &*(i - 1)

( Dr.Dobbs makalesinden ):

alternatif metin

Bu yüzden base () öğesini alırken bir ofset uygulamanız gerekir. Bu nedenle çözüm:

m_CursorStack.erase( --(i.base()) );

DÜZENLE

C ++ 11 için güncelleme.

reverse_iterator ideğişmedi:

m_CursorStack.erase( std::next(i).base() );

reverse_iterator igelişmiş:

std::advance(i, 1);
m_CursorStack.erase( i.base() );

Bunu önceki çözümümden çok daha net buluyorum. İstediğinizi kullanın.


27
Alıntı yaptığınız makalenin biraz daha fazlasını not etmelisiniz - taşınabilir olması için ifadenin olması gerekir m_CursorStack.erase( (++i).base())(adam, bu şeyleri ters yineleyicilerle yapmak başımı incitir ...). DDJ makalesinin Meyer'in "Etkili STL" kitabına dahil edildiği de not edilmelidir.
Michael Burr

8
Bu diyagramı yardımcı olmaktan daha kafa karıştırıcı buluyorum. Rbegin, ri ve rend, aslında işaret ettikleri şeyin sağındaki öğeye işaret ediyor. Diyagram, eğer siz *onlara erişirseniz hangi öğeye erişeceğinizi gösterir , ancak eğer onlara bakarsanız hangi öğeye işaret edeceğinizi base, yani sağdaki bir öğeden bahsediyoruz . Böyle bir hayranıyım değilim --(i.base())ya (++i).base()da yineleyici mutasyona beri çözümleri. (i+1).base()Hangisinin işe yaradığını da tercih ederim .
mgiuca

4
Ters yineleyiciler yalancıdır .. ertelendiğinde, ters yineleyici öğeyi ondan önce döndürür . Buraya bakınız
bobobobo

4
Kesinlikle açık olmak gerekirse, bu teknik hala normal bir döngü için kullanılamaz (yineleyicinin normal şekilde artırıldığı yerlerde). Bkz. Stackoverflow.com/questions/37005449/…
logidelic

1
m_CursorStack.erase ((++ i) .base ()) ++ i son öğeyi geçmenizi sağlarsa bir sorun olurdu gibi görünüyor. Sonunda () silme işlemini çağırabilir misiniz?
stu

16

m_CursorStack.erase( (++i).base())Bir fordöngüde kullanılırsa (orijinal soruya bakın) bir sorun olabileceğini lütfen unutmayın, çünkü i değerini değiştirir. Doğru ifade:m_CursorStack.erase((i+1).base())


4
Yineleyicinin bir kopyasını oluşturmanız ve yapmanız gerekir iterator j = i ; ++j, çünkü i+1yineleyici üzerinde çalışmaz, ancak bu doğru fikir
bobobobo

3
@bobobobo, m_CursorStack.erase(boost::next(i).base())Boost ile kullanabilirsiniz . veya C ++ 11m_CursorStack.erase(std::next(i).base())
alfC

12

... veya bu öğeyi listeden çıkarmanın başka bir yolu var mı?

Bu, -std=c++11(için auto) bayrağını gerektirir :

auto it=vt.end();
while (it>vt.begin())
{
    it--;
    if (*it == pCursor) //{ delete *it;
        it = vt.erase(it); //}
}

Bir cazibe çalışır :)
Den-Jason

@GaetanoMendola: neden?
slashmais

3
Bir listedeki yineleyicilerin sipariş edildiğini kim garanti eder?
Gaetano Mendola

7

Bu sayfada henüz doğru bir çözüm bulunmadığı komik. Yani, aşağıdakiler doğrudur:

İleri yineleyici durumunda, çözüm doğrudan ileri:

std::list< int >::iterator i = myList.begin();
while ( ; i != myList.end(); ) {
  if ( *i == to_delete ) {
    i = myList.erase( i );
  } else {
    ++i;
  } 
}

Ters yineleyici durumunda aynı şeyi yapmanız gerekir:

std::list< int >::reverse_iterator i = myList.rbegin();
while ( ; i != myList.rend(); ) {
  if ( *i == to_delete ) {
    i = decltype(i)(myList.erase( std::next(i).base() ));
  } else {
    ++i;
  } 
}

Notlar:

  • Bir reverse_iterator bir İlerleticiden
  • Dönüş değerini kullanabilirsiniz std::list::erase

Bu kod işe yarıyor, ancak bir sonraki yinelemenin neden kullanıldığını ve dünya çökmeden bir ters yineleyiciyi ters çevirmenin nasıl güvenli olduğunu açıklayın
Lefteris E

1
@LefterisE Bu oyuncu kadrosu değil. Bir interator'dan yeni bir ters yineleyici oluşturur. Bu, ters yineleyicinin normal kurucusudur.
Šimon Tóth

3

reverse_iterator'S base()yöntemini kullanırken ve sonucu azaltmak burada çalışırken , reverse_iterators'nin normal iterators ile aynı statüye sahip olmadığına dikkat etmek önemlidir . Genel olarak, bunun gibi nedenlerden ötürü düzenli iterators'yi reverse_iterators'ye (ayrıca const_iterators ve const_reverse_iterators'ye) tercih etmelisiniz . Nedenini derinlemesine tartışmak için Doktor Dobbs Dergisi'ne bakınız .


3
typedef std::map<size_t, some_class*> TMap;
TMap Map;
.......

for( TMap::const_reverse_iterator It = Map.rbegin(), end = Map.rend(); It != end; It++ )
{
    TMap::const_iterator Obsolete = It.base();   // conversion into const_iterator
    It++;
    Map.erase( Obsolete );
    It--;
}

3

Ve burada, tersine yineleme yaparken bir kaptaki bir öğeyi silmek için silme sonucunu ters bir yineleyiciye dönüştüren kod parçası. Biraz garip, ancak ilk veya son öğeyi silerken bile çalışır:

std::set<int> set{1,2,3,4,5};

for (auto itr = set.rbegin(); itr != set.rend(); )
{    
    if (*itr == 3)
    {
        auto it = set.erase(--itr.base());
        itr = std::reverse_iterator(it);            
    }
    else
        ++itr;
}

2

İlerlerken her şeyi silmeniz gerekmiyorsa, sorunu çözmek için silme-kaldırma deyimini kullanabilirsiniz:

m_CursorStack.erase(std::remove(m_CursorStack.begin(), m_CursorStack.end(), pCursor), m_CursorStack.end());

std::removekaptaki pCursorsonuna kadar eşleşen tüm öğeleri değiştirir ve ilk eşleme öğesine bir yineleyici döndürür. Ardından, erasebir aralık kullanıldığında ilk eşleşme silinir ve sonuna kadar gider. Eşleşmeyen öğelerin sırası korunur.

Bu, sizin için daha hızlı sonuç verebilir. std::vectorİçeriğin ortasındaki silme işleminin çok fazla kopyalama veya taşıma gerektirebileceği bir , verebilir.

Ya da tabii ki, yukarıdaki kullanımı açıklayan cevaplar reverse_iterator::base()ilginç ve bilmeye değer, belirtilen kesin sorunu çözmek için std::remove, bunun daha uygun olduğunu iddia ediyorum .


1

Sadece bir şeyi açıklığa kavuşturmak istedim: Yukarıdaki yorumların ve cevapların bazılarında silme için taşınabilir sürüm (++ i) .base () olarak adlandırılır. Ancak bir şey eksik sürece doğru ifade (++ ri) .base (), yani 'ters' yineleyici (yineleyici) 'anlamına gelir.

Dün benzer bir şey yapmaya ihtiyacım vardı ve bu yazı yardımcı oldu. Herkese teşekkürler.


0

Başkalarının cevaplarını tamamlamak için ve std :: string hakkında çok başarılı olmadan arama yaparken bu soruya tökezlediğim için, burada std :: string, std :: string :: erase ve std :: reverse_iterator kullanımı ile bir yanıt gidiyor

Benim sorunum tam bir dosya adı dizesinden bir görüntü dosya adını silmek oldu. Başlangıçta std :: string :: find_last_of ile çözüldü, ancak std :: reverse_iterator ile alternatif bir yol araştırıyorum.

std::string haystack("\\\\UNC\\complete\\file\\path.exe");
auto&& it = std::find_if( std::rbegin(haystack), std::rend(haystack), []( char ch){ return ch == '\\'; } );
auto&& it2 = std::string::iterator( std::begin( haystack ) + std::distance(it, std::rend(haystack)) );
haystack.erase(it2, std::end(haystack));
std::cout << haystack;  ////// prints: '\\UNC\complete\file\'

Bu algoritma, yineleyici ve dize üstbilgilerini kullanır.


0

ters yineleyiciyi kullanmak oldukça zordur. Yani sadece kullanılan genel yineleyici. 'r' Son elemandan başlar. Silmek için bir şey bulduğunuzda. silin ve sonraki yineleyiciye dönün. örneğin 3. elemanı sildiğinizde mevcut 4. elemanı gösterecektir. ve yeni 3. Sola gitmek için 1 azaltılmalı

void remchar(string& s,char c)
{      
    auto r = s.end() - 1;
    while (r >= s.begin() && *r == c)
    {
        r = s.erase(r);
        r -= 1;
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.