C ++에서 작업 설정 (기존 값 업데이트)

내 코드는 다음과 같습니다.

 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 ++ 방식은 아닙니다.

옵션은 다음과 같습니다.

  1. 다른 유형의 컬렉션 (정렬되지 않은)을 사용하십시오.
  2. 새 세트를 작성하고 수정 된 요소로 채 웁니다.
  3. 에서 요소를 제거하고 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이 시나리오를 극복 하는 데 사용할 수 있습니다 .