Set operation in c++(update existing value)

Key values of elements in a std::set are const for a good reason. Modifying them may destroy the order which is essential for a std::set.

Hence, the solution is to erase the iterator and insert a new one with key *it - sub. Please, note that std::set::erase() returns a new iterator which has to be used in your case to keep the while loop working properly.

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

Output:

test: { 11, 12, 13, 14, 15 }
test: { 1, 2, 3, 4, 5 }

Live Demo on coliru


Changes on the std::set while iterating over it are not a problem in general but can cause subtle issues.

The most important fact is that all used iterators have to be kept intact or may not be used anymore. (That's why the current iterator of the erase element is assigned with the return value of std::set::erase() which is either an intact iterator or the end of set.)

Of course, elements can be inserted as well behind the current iterator. While this is not a problem concerning the std::set it may break the loop of my above example.

To demonstrate it, I changed the above sample a bit. Please, note that I added an additional counter to grant the termination of loop:

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

Output:

test: { 11, 12, 13, 14, 15 }
test: { 23, 24, 25, 31, 32 }

Live Demo on coliru


Simple to just replace it with another set

std::set<int> copy;

for (auto i : s)
    copy.insert(i - sub);

s.swap(copy);

You can't mutate elements of std::set by design. See

https://en.cppreference.com/w/cpp/container/set/begin

Because both iterator and const_iterator are constant iterators (and may in fact be the same type), it is not possible to mutate the elements of the container through an iterator returned by any of these member functions.

That is because set is sorted. If you mutate element in a sorted collection the collection must be sorted again, which is of course possible, but not the C++ way.

Your options are:

  1. Use another type of collection (unsorted).
  2. Create a new set and fill it with modified elements.
  3. Remove an element from std::set, modify it, then insert again. (It is not a good idea if you want to modify every element)

Tags:

C++