내 코드는 다음과 같습니다.
while (it!=s.end()) //here 's' is a set of stl and 'it' is iterator of set
{
*it=*it-sub; //'sub' is an int value
it++;
}
반복자가 설정 한 값을 업데이트 할 수 없습니다. set의 모든 요소에서 정수 값 ‘sub’를 빼고 싶습니다.
실제 문제가있는 곳과 실제 해결책은 무엇입니까?
오류 메시지는 다음과 같습니다.
error: assignment of read-only location ‘it.std::_Rb_tree_const_iterator<int>::operator*()’
28 | *it=*it-sub;
| ~~~^~~~~~~~
답변
A의 원소의 키 값 std::set
입니다 const
좋은 이유. 수정하면에 필수적인 순서가 손상 될 수 있습니다 std::set
.
따라서 해결책은 반복자를 지우고 키를 사용하여 새 반복자를 삽입하는 것입니다 *it - sub
. std::set::erase()
while 루프가 제대로 작동하도록하려면 새 iterator 를 반환해야합니다.
#include<iostream>
#include<set>
template <typename T>
std::ostream& operator<<(std::ostream &out, const std::set<T> &values)
{
const char *sep = "{ ";
for (const T &value : values) { out << sep << value; sep = ", "; }
return out << " }";
}
int main()
{
std::set<int> test{ 11, 12, 13, 14, 15 };
std::cout << "test: " << test << '\n';
const int sub = 10;
std::set<int>::iterator iter = test.begin();
while (iter != test.end()) {
const int value = *iter;
iter = test.erase(iter);
test.insert(value - sub);
}
std::cout << "test: " << test << '\n';
}
산출:
test: { 11, 12, 13, 14, 15 }
test: { 1, 2, 3, 4, 5 }
std::set
반복 하는 동안의 변경 은 일반적으로 문제가 아니지만 미묘한 문제를 일으킬 수 있습니다.
가장 중요한 사실은 사용 된 모든 반복자가 그대로 유지되거나 더 이상 사용되지 않아야한다는 것입니다. (따라서 지우기 요소의 현재 반복자에 반환 값이 std::set::erase()
손상되지 않은 반복자이거나 세트의 끝인 것이 지정됩니다.)
물론 현재 반복자 뒤에 요소를 삽입 할 수 있습니다. 이것은 이것에 관한 문제는 아니지만 std::set
위 예제의 루프를 깨뜨릴 수 있습니다.
이를 입증하기 위해 위의 샘플을 약간 변경했습니다. 루프 종료를 허용하기 위해 카운터를 추가했습니다.
#include<iostream>
#include<set>
template <typename T>
std::ostream& operator<<(std::ostream &out, const std::set<T> &values)
{
const char *sep = "{ ";
for (const T &value : values) { out << sep << value; sep = ", "; }
return out << " }";
}
int main()
{
std::set<int> test{ 11, 12, 13, 14, 15 };
std::cout << "test: " << test << '\n';
const int add = 10;
std::set<int>::iterator iter = test.begin();
int n = 7;
while (iter != test.end()) {
if (n-- > 0) {
const int value = *iter;
iter = test.erase(iter);
test.insert(value + add);
} else ++iter;
}
std::cout << "test: " << test << '\n';
}
산출:
test: { 11, 12, 13, 14, 15 }
test: { 23, 24, 25, 31, 32 }
답변
다른 세트로 교체하는 것이 간단합니다.
std::set<int> copy;
for (auto i : s)
copy.insert(i - sub);
s.swap(copy);
답변
std::set
의도적으로 요소를 변경할 수 없습니다 . 보다
https://en.cppreference.com/w/cpp/container/set/begin
iterator와 const_iterator는 모두 일정한 반복자 (실제로 같은 유형일 수 있음)이기 때문에 이러한 멤버 함수 중 하나에 의해 리턴 된 반복자를 통해 컨테이너 요소를 변경할 수 없습니다.
set이 정렬 되었기 때문입니다 . 정렬 된 컬렉션에서 요소를 변경하면 컬렉션을 다시 정렬해야합니다. 물론 가능하지만 C ++ 방식은 아닙니다.
옵션은 다음과 같습니다.
- 다른 유형의 컬렉션 (정렬되지 않은)을 사용하십시오.
- 새 세트를 작성하고 수정 된 요소로 채 웁니다.
- 에서 요소를 제거하고
std::set
수정 한 다음 다시 삽입하십시오. (모든 요소를 수정하려는 경우 좋지 않습니다)
답변
ㅏ std::set
는 일반적으로 STL에서 자체 균형 이진 트리로 구현됩니다. *it
트리를 주문하는 데 사용되는 요소의 값입니다. 주문을 수정할 수 있으면 주문이 유효하지 않으므로 그렇게 할 수 없습니다.
요소를 업데이트하려면 세트에서 해당 요소를 찾아 제거하고 업데이트 된 요소 값을 삽입해야합니다. 그러나 모든 요소의 값을 업데이트해야하므로 모든 요소를 하나씩 지우고 삽입해야합니다.
제공된 for 루프로 수행 할 수 있습니다 sub > 0
. S.erase(pos)
위치에서 반복자를 제거하고 pos
다음 위치를 리턴합니다. 인 경우 sub > 0
삽입 할 업데이트 된 값이 트리의 새 반복기의 값보다 앞에 오지만 인 경우 sub <= 0
업데이트 된 값은 트리의 새 반복기의 값 다음에 오므로 결과적으로 무한 루프.
for (auto itr = S.begin(); itr != S.end(); )
{
int val = *itr;
itr = S.erase(itr);
S.insert(val - sub);
}
답변
오류는 거의 문제를 설명합니다
std::set
컨테이너 멤버 는 const
입니다. 변경하면 해당 주문이 유효하지 않습니다.
의 요소를 변경 std::set
하려면 항목을 지우고 변경 한 후 다시 삽입해야합니다.
또는 std::map
이 시나리오를 극복 하는 데 사용할 수 있습니다 .