역 반복자를 사용하여 지우기를 호출하는 방법


181

나는 이런 식으로하려고합니다.

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

그러나 지우기는 역 반복기가 아닌 반복기를 사용합니다. 역 이터레이터를 일반 이터레이터로 변환하는 방법이나 목록에서이 요소를 제거하는 다른 방법이 있습니까?


17
따로, 이와 같은 루프를 작성할 때 여기에서와 같이 끝 반복기를 반복적으로 계산하지 마십시오 i != m_CursorStack.rend(). 대신에을 쓰십시오 i = m_CursorStack.rbegin(), end = m_CursorStack.rend(); i != end;. 즉, 루프 바디의 부작용으로 끝 위치가 변경되지 않는다고 가정 할 때 반복 비교를 위해 유지할 수있는 반복자를 초기화하십시오.
seh

여기서 분명한 질문은 왜 당신이 이것을하고 있는지에 대한 것 같습니다. 목록을 거꾸로 횡단하면 무엇을 얻을 수 있습니까? 이 코드를 사용하는 대신 직접 작성하면 무엇을 얻을 수 std::remove있습니까?
Jerry Coffin

그리고 std :: list의 반복자는 참조하는 요소가 지워진 후에도 계속 증가합니까?
Steve Jessop

3
하나의 요소 만 제거하므로 'break;', 'remove'를 사용하면 일치하는 시간이 오래 걸리고 원하는 것을 수행하지 않는 것이 제거됩니다. 이 특정 경우에 제거하려는 요소는 거의 항상 목록의 끝이거나 그에 매우 가깝기 때문에 역순으로 반복하는 것이 문제에 더 빠르고 더 적합합니다.
0xC0DEFACE

4
stackoverflow.com/a/2160581/12386 이것은 사용자가 알거나 신경 쓰지 않아야하기 때문에 디자이너가 구현을 구체적으로 정의하지 않는다고 말하지만 @seh는 위의 rend ()가 계산된다는 것을 마술처럼 알기를 기대합니다. 그리고 비싸다.
stu mar

답변:


181

더 많은 연구와 테스트를 거쳐 솔루션을 찾았습니다. 분명히 표준 [24.4.1 / 1]에 따르면 i.base ()와 i의 관계는 다음과 같습니다.

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

( 닥스 돕스 기사에서 ) :

대체 텍스트

따라서 base ()를 가져올 때 오프셋을 적용해야합니다. 따라서 해결책은 다음과 같습니다.

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

편집하다

C ++ 11 업데이트

reverse_iterator i는 변경되지 않았습니다.

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

reverse_iterator i가 고급입니다.

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

이전 솔루션보다 훨씬 명확합니다. 필요한 것을 사용하십시오.


27
당신은 당신이 인용 한 기사를 조금 더 주목해야한다-표현이 이식성이 있어야한다 m_CursorStack.erase( (++i).base()). 또한 DDJ 기사는 Meyer의 "Effective STL"책에 포함되어 있습니다.
Michael Burr

8
나는 그 다이어그램이 도움이되는 것보다 더 혼란 스럽다는 것을 안다. rbegin, ri 및 rend는 모두 실제로 가리키는 요소의 오른쪽을 가리키는 요소입니다. 다이어그램은 사용자가 액세스 할 때 액세스 할 요소를 보여 *주지만 base, 오른쪽 요소 중 하나 인 요소를 가리키고 있습니다 . 나는 그런의 팬이 아니에요 --(i.base())또는 (++i).base()그들이 반복자를 변이 때문에 솔루션을 제공합니다. 내가 선호 (i+1).base()뿐만 아니라 작동한다.
mgiuca

4
역 이터레이터는 거짓말 쟁이입니다. 지연 될 때 역 이터레이터는 요소 를 그 전에 반환 합니다 . 여기를보십시오
bobobobo

4
절대적으로 명확하게하기 위해이 기술은 여전히 ​​정상적인 for 루프 (반복자가 일반적인 방식으로 증가하는 경우)에서 사용할 수 없습니다. 참조 stackoverflow.com/questions/37005449/...
logidelic

1
m_CursorStack.erase ((++ i) .base ())는 ++ i가 마지막 요소를지나 치면 문제가되는 것처럼 보입니다. end ()에 지우기를 호출 할 수 있습니까?
stu mar

16

참고 m_CursorStack.erase( (++i).base())A의 사용하는 경우 문제가 될 수 for는 i 값을 변경하기 때문에 (원래의 질문 참조) 루프를. 올바른 표현은m_CursorStack.erase((i+1).base())


4
반복자에서 작동하지 않기 iterator j = i ; ++j때문에 반복자의 사본을 작성해야 i+1합니다. 그러나 그것이 올바른 생각입니다.
bobobobo

3
@bobobobo, m_CursorStack.erase(boost::next(i).base())Boost와 함께 사용할 수 있습니다 . 또는 C ++ 11m_CursorStack.erase(std::next(i).base())
alfC

12

... 또는이 요소를 목록에서 제거하는 다른 방법은 무엇입니까?

-std=c++11플래그에는 (for auto) 이 필요합니다 .

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

매력을 작동 :)
Den-Jason

@GaetanoMendola : 왜?
slashmais

3
목록의 반복자가 주문되었다고 누가 보증합니까?
Gaetano Mendola

7

이 페이지에 아직 올바른 해결책이 없다는 것이 유감입니다. 따라서 다음은 올바른 것입니다.

순방향 반복자의 경우 솔루션은 간단합니다.

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

역 이터레이터의 경우 동일한 작업을 수행해야합니다.

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;
  } 
}

노트:

  • 당신은을 구성 할 수 reverse_iterator반복자에서
  • 의 반환 값을 사용할 수 있습니다 std::list::erase

이 코드는 작동하지만 다음 코드가 사용되는 이유와 세계 붕괴없이 순방향 반복자를 역방향 반복자로 캐스팅하는 것이 어떻게 안전한지 설명하십시오
Lefteris E

1
@LefterisE 캐스트가 아닙니다. 인터 레이터에서 새로운 리버스 이터레이터를 만듭니다. 이것은 역 반복자의 정상적인 생성자입니다.
Šimon Tóth

3

reverse_iteratorbase()방법을 사용하고 결과를 줄이는 것이 여기에서 효과가 있지만 reverse_iterators에 일반 iterators 와 동일한 상태가 부여되지 않았다는 점은 주목할 가치가 있습니다. 일반적으로 이와 같은 이유로 일반 iterators reverse_iterator에서 const_iterators 및 const_reverse_iterators 및 s 를 선호해야합니다 . 이유에 대한 자세한 내용은 Dr Dobbs Journal 을 참조하십시오 .


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

그리고 여기에 삭제의 결과를 역 반복자로 다시 변환하여 컨테이너에서 요소를 지우고 반대로 반복하는 코드가 있습니다. 조금 이상하지만 첫 번째 또는 마지막 요소를 지우는 경우에도 작동합니다.

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

진행하면서 모든 것을 지울 필요가 없다면 문제를 해결하기 위해 지우기-삭제 관용구를 사용할 수 있습니다.

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

std::remove컨테이너 pCursor의 끝 과 일치하는 모든 항목을 교체 하고 첫 번째 일치 항목으로 반복자를 반환합니다. 그런 다음 erase범위를 사용하면 첫 번째 일치 항목이 지워지고 끝으로 이동합니다. 일치하지 않는 요소의 순서는 유지됩니다.

를 사용하는 std::vector경우 콘텐츠 중간에 지우는 데 많은 복사 또는 이동이 필요할 수 있습니다.

또는 물론, 사용법을 설명하는 위의 답변 reverse_iterator::base()은 흥미롭고 알만한 가치가 있으며, 언급 된 정확한 문제를 해결하기 위해 std::remove더 적합 하다고 주장합니다 .


1

위의 설명 중 일부에서 지우기를위한 이식 가능한 버전은 (++ i) .base ()로 언급됩니다. 그러나 내가 빠진 것이 없으면 올바른 문장은 (++ ri) .base ()입니다. 반복자가 아닌 reverse_iterator를 '증가'한다는 의미입니다.

나는 어제 비슷한 일을 할 필요가 있었고이 게시물이 도움이되었습니다. 모두 감사합니다.


0

다른 답변을 보완하고 많은 성공없이 std :: string을 검색하는 동안이 질문에 걸려 넘어 졌기 때문에 std :: string, std :: string :: erase 및 std :: reverse_iterator 사용법에 대한 응답이 있습니다.

내 문제는 완전한 파일 이름 문자열에서 이미지 파일 이름을 지우는 것이 었습니다. 원래 std :: string :: find_last_of로 해결되었지만 std :: reverse_iterator를 사용하는 다른 방법을 연구합니다.

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\'

알고리즘, 반복자 및 문자열 헤더를 사용합니다.


0

역 반복자는 사용하기가 매우 어렵습니다. 그래서 그냥 일반적인 반복자를 사용했습니다. 'r'마지막 요소부터 시작합니다. 지울 무언가를 찾으면. 지우고 다음 반복기를 반환하십시오. 예를 들어 3 번째 요소를 삭제하면 현재 4 번째 요소를 가리 킵니다. 그리고 새로운 3 위. 왼쪽으로 이동하려면 1을 줄여야합니다

void remchar(string& s,char c)
{      
    auto r = s.end() - 1;
    while (r >= s.begin() && *r == c)
    {
        r = s.erase(r);
        r -= 1;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.