2011-01-07 3 views
2

STL 컨테이너 세트 및 맵은 엄격한 약한 순서로 요소를 제공한다고 생각했습니다. 그러나, 역 참조를 통해 요소의 값을 찾고 변경하여 반복기를 얻는다면 순서를 복원하지 못하는 것으로 밝혀졌습니다. 이는 23.1.2.2 및 23.3.3.2를 위반합니다. 그것은 (2008 년 MS VS) STL의 내 버전의 버그세트의 엄격한 약한 순서 매기기 감소

5 10 20 30 
5 3 20 30 
5 3 20 30 40 

가 : 여기

int nv = 3; 
set<int> s = set<int>(); 
s.insert(5); 
s.insert(10); 
s.insert(20); 
s.insert(30); 
for(set<int>::const_iterator cit = s.begin(); cit != s.end(); ++cit) 
    cout<<*cit<<" "; 
cout <<endl; 
set<int>::iterator it = s.find(10); 
*it = nv; 
for(set<int>::const_iterator cit = s.begin(); cit != s.end(); ++cit) 
    cout<<*cit<<" "; 
cout <<endl; 
s.insert(40); 
for(set<int>::const_iterator cit = s.begin(); cit != s.end(); ++cit) 
    cout<<*cit<<" "; 
cout <<endl; 

가 생성하는 코드는? 아니면 내가 틀렸어?

답변

4

집합의 iterator와 const_iterator는 모두 상수 반복기입니다. 세트의 값은 수정하고 삽입 및 제거 만 할 수 있습니다.

+1

아니요, 위 코드가 컴파일, 링크 및 실행됩니다. – flashnik

+2

컴파일러 또는 라이브러리가 손상된 경우에만 컴파일 할 수 있습니다. 예를 들어, 현재 초안 23.2.4/6 : " 값 유형이 키 유형과 동일한 연관 컨테이너의 경우 iterator와 const_iterator는 모두 상수 반복자입니다." –

+0

@flashnik : 예 STL 또는 컴파일러의 구현에서 버그가 있습니다. 제 의견으로는 컴파일이 허용되어서는 안됩니다. –

4

std::mapstd::set은 삽입시에만 순서 지정을 수행합니다. 기존 항목의 키/값을 변경하면 마술은 일어나지 않으며 결과는 정의되지 않은 것입니다.

원하는 효과를 얻으려면 원래 요소를 제거하고 새 요소를 삽입해야합니다.

+0

지도, 키를 변경할 수 없습니다. 예를 들어, 다음 삽입 세트에서 순서를 복원하는 것이 좋겠지 만 그런 일은 발생하지 않았습니다. 결과적으로,이 세트는 무질서를 가지고 있습니다. ( – flashnik

+0

@flashnik : 매 시간마다 전체 트리의 순서를 바꾸는지도 같은 컨테이너를 작성할 수는 있지만 느리게 진행될 것입니다. –

+0

컴파일을하고 싶습니다./런타임 오류 또는 위 코드의 예외 : ( – flashnik

0

std::mapstd::set엄격한 약한 주문, 그들은 하나를 필요로 제공하지 않습니다. 주문을 제공해야합니다 (기본값은 에 해당하는 std::less입니다).

set 또는 map의 요소가 변경되는 방식을 변경하면 주문이 안정적이지 않아 요구 사항을 위반하게됩니다.

2

해당 STL 버전의 버그처럼 보입니다.
t2.cpp:18:8: error: assignment of read-only location ‘it.std::_Rb_tree_const_iterator<_Tp>::operator* [with _Tp = int, const _Tp& = const int&]()’

+0

감사합니다. gcc를 확인하지 않았습니다. 그들이 오류로 취급하는 것은 매우 좋습니다. – flashnik

1

일반적인 경우에 당신이 반복자를 통해 연관 컨테이너 요소의 키 부분을 수정할 수 없습니다 : 나는 다음과 같은 오류가 발생 ++ 내 g에

. 코드는 컴파일되지 않습니다.

귀하의 표준 라이브러리 구현에서 *it = nv 할당을 허용하는 경우 해당 구현의 버크이어야합니다. AFAIK,이 할당이 컴파일되도록 허용하는 것은 엄격히 불법이 아닙니다. 그것은 구현의 품질 문제입니다.

Comeau 구현으로 할당이 컴파일되지 않습니다. MS 구현은 분명히 덜 제한적입니다.

1

std::set의 요소를 변경하면 내부 순서가 위반되고 데이터 구조가 손상 될 수 있습니다. 일부 STL 구현 만이 iterator이 가리키는 것을 수정하지 못하도록 방지하므로이 제약 조건을 직접 적용해야합니다. 당신이 그것을 할 수 있지만, 그것은 당신의 std::vector 손상거야 -

그것은 reserve은 (공간을 만들기 위해) 호출하여 std::vector의 논리적 끝을지나 작성 및 포인터 연산을 사용하는 것과 같습니다.당신이 세트의 요소를 변경하려면

이 작동합니다 :

std::set<int> my_set; 

// initialize my_set ... 

std::set<int>::iterator itr = my_set.find(10); 
if (itr != my_set.end()) { 
    my_set.erase(itr++); // ++ avoids invalidating iterator 
    my_set.insert(3); 
} 

는 당신은 후행 증가하는 itr을 당신이 그것을 계속 사용하려는 경우 필요합니다.

(출처 : 효과적인 STL "항목 (22)는,"스콧 마이어스)는 '쌍 `로 반복자를 역 참조하기 때문에

+0

C++은 다리에서 자신을 쏠 수있는 최고의 언어입니다. 인용 해 주셔서 감사합니다. – flashnik

+0

괜찮습니다! 나는 그의 책 3 권 모두를 강력히 추천한다. 그들은 C++의 다양한 측면에서 수많은 함정을 탐구합니다. – Dawson

+0

나는 그것들을 읽었으나 그런 경우는 기억하지 못했다. 앞에서 언급했듯이, 그러한 코드를 작성하는 것은 금지되었지만 Gene Bushuyev가 지적한 것처럼 C++ 0x에서만 나타나고 일부 컴파일러는 금지되어 있습니다. – flashnik