2010-12-07 1 views
0

한 세트의 요소가 반복자를 허용하는 erase 메소드를 사용하여 다른 세트에서 삭제 될 수 있다는 것을 알게되었습니다. 왜 이런 일이 일어날 지 설명 할 수 없으며 이것이 정상적인 행동인지 아닌지 알고 싶습니다. 난 논리적 인 이유를 들어, 두 개의 STL 세트에서 분리 된 개체 집합 (즉, 그들은 개별 세트입니다)가다른 세트에서 erase (iterator)를 호출하여 한 세트에서 요소를 삭제합니다. 이것은 정상적인 행동입니까?

:

그래서, 장면을 설정할 수 있습니다.

std::set<Obj> set_a; 
std::set<Obj> set_b; 

set_a 내가 원하는 개체를 포함합니다. 이 객체가 set_a에서 발견되지 않으면 빈 객체가 만들어져 set_b에 삽입됩니다. 그런 다음 원하는 객체에 대해 몇 가지 계산을 수행하고 특정 조건이 충족되면 삭제합니다.

std::set<Obj>::iterator it = set_a.find(o); 
std::set<Obj>::iterator end = set_a.end(); 

if (it == end) { 
    it = set_b.lower_bound(); 
    end = set_b.end(); 

    if (it == end || *it != o) { 
     it = set_b.insert(it, o); 
    } 
} 

// do calculations with it 
if (/*condition is met*/) { 
    // erase it 
} 

그래서 나는이 개체를 삭제 얼마나 궁금 해서요. 필자는 반복기를 사용하므로 직접 사용하여 삭제할 생각을했습니다. 그러나 반복자를 지우개를 사용하여 다른 세트의 객체를 "가리키는"경우에는 어떤 일이 발생합니까? 내가 사용하는 문서 (http://www.cplusplus.com/reference/stl/set/erase/)에는 아무 것도 없으므로 다음 테스트를 수행했습니다. 다음과 같은 종료에

#include <iostream> 
#include <iterator> 
#include <algorithm> 
#include <set> 
#include <sstream> 

// streams 
using std::cout; 
using std::ostream_iterator; 
using std::ostringstream; 
// data structures 
using std::set; 
// algorithms 
using std::copy; 

int main() { 
    set<int> s, s2; 
    s.insert(1); 
    s.insert(2); 
    s.insert(4); 

    cout << "Initial set\n"; 
    // print set elements 
    copy(s.begin(), s.end(), ostream_iterator<int> (cout, " ")); 
    cout << "\n"; 

    set<int>::iterator s_it = s.lower_bound(3); 

    if (s_it == s.end() || *s_it != 3) { 
     s_it = s.insert(s_it, 3); 
    } 

    cout << "Set after insertion\n"; 
    // print set elements 
    copy(s.begin(), s.end(), ostream_iterator<int> (cout, " ")); 
    cout << "\n"; 

    // erase element from another set 
    s2.erase(s_it); 

    cout << "Set after erasure\n"; 
    // print set elements 
    copy(s.begin(), s.end(), ostream_iterator<int> (cout, " ")); 
    cout << "\n"; 

    return 0; 
} 

이 테스트 결과 :

 
Initial set 
1 2 4 
Set after insertion 
1 2 3 4 
Set after erasure 
1 2 4 

나는 s의 요소가 (S2)에서 메소드를 호출에 의해 삭제 될 수 있음을 매우 이상한 발견, 그래서 나는 내 프로그램을 실행 valgrind. 오류가 없습니다. 어떤 세트에 요소가 들어 있는지 확인할 필요가 없기 때문에 실제로이 동작의 이점을 얻을 것이라고 생각합니다. 그러나 이것이 정상적인 동작인지 아닌지 궁금합니다.

감사합니다!

+0

내가 결론을 내릴지 확실하지 않습니다. 요소를 s에 넣고 s에서 요소를 가리키는 반복자를 사용하여 요소를 삭제합니다. s2는 정말로 아무 것도하지 않았습니다. –

+0

@Anon, s에서 반복자를 사용하여 s2.erase()를 호출하면 s를 제공하는 것이 좋습니다. s2는 동일한 유형입니까? –

+0

@ 토니 : 나는 그것을 말하고 있지 않다. 나는 "B의 반복자에서 A의 요소를 삭제하는 것"이 ​​여기서 일어나지 않는다고 말하고 있습니다. –

답변

8

이것은 공급 업체가 std::set<T>std::set<T>::iterator을 구현하는 방법으로 인해 작동하지만 보장 할 수 없습니다.

표준 섹션 23.1.2 단락 7 및 표 69 a는 연관 컨테이너 클래스 타입의 객체 (set는 연관 컨테이너) 및 "qa 유효한 dereferenceable 반복기를 나타내고"인 경우 식 a.erase(q) 유효라고 .

반복기가 컨테이너에 있지 않으면 정의되지 않은 동작이 발생합니다. 일을하는 것은 정의되지 않은 행동의 한 가지 유효한 결과입니다.

+1

+1 표준 인용. – Macke

+1

정의되지 않은 동작에 의존하는 코드 작성에주의하십시오. 절대로 잘 끝나지 않습니다. –

+0

정말 명확한 답변을 주셔서 감사합니다! 실제로이 코드를 사용하지는 않았지만이 이상한 동작은 실제로 나를 생각하게 만들었 기 때문에 물어볼 필요가있었습니다 :) 빠른 질문 : 비록 어디에서 표준을 찾을 수 있습니까? 나는 많은 인용문을 보았지만 여전히 찾을 수 없었다. – pedromanoel

0

표준에 로컬 포인터를 사용하는 방법 :: set < Obj>?

std::set<Obj> set_a; 
std::set<Obj> set_b; 
std::set<Obj>* current_set = &set_a; 

std::set<Obj>::iterator it = current_set->find(o); 
std::set<Obj>::iterator end = current_set->end(); 

if (it == end) { 
    current_set = &set_b; 
    it = current_set->lower_bound(); 
    end = current_set->end(); 

    if (it == end || *it != o) { 
     it = current_set->insert(it, o); 
    } 
} 

// do calculations with it 
if (/* condition met */) { 
    current_set->erase(it); 
} 
+2

나는 당신이 참조를 사용하고 싶지 않다고 생각합니까? 그것은 당신이'current_set'을 다시 채우려고하는 것 같아요.'set_a'를'set_b'로 덮어 쓸'operator =()'를 실제로 호출하게 될 것 같아요. –

+0

나는 정말로 질문에 답하는 것이 아니라 대답을 시도하는 것보다는 내 학습을해야한다고 생각합니다. 참조 대신 포인터를 사용하는 편집 된 대답은 ... 어떻게됩니까? –

2

나는 다른 컨테이너에서 반복자는이 방법으로 혼합되어서는 안된다는 상당히 확신, 그것은 지칠대로 지친 정의되지 않은 동작 결과 있음.

그래서, 당신이 보는 것은 순수한 운과 기회입니다. 이것은 컴파일러의 STL 구현과 달의 현재 단계에서 작동합니다. 언제 어디서나 다른 곳에서 일하는 것에 내기를 걸지는 않을 것입니다.

관련 문제