Вот мой код:
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++;
}
Я не могу обновить значение, установленное итератором. Я хочу вычесть целочисленное значение 'sub' из всех элементов множества.
Может ли кто-нибудь помочь мне, где актуальная проблема и каково будет реальное решение?
Вот сообщение об ошибке:
error: assignment of read-only location ‘it.std::_Rb_tree_const_iterator<int>::operator*()’
28 | *it=*it-sub;
| ~~~^~~~~~~~
*it - sub
. Пожалуйста, обратите внимание, чтоstd::set::erase()
возвращает новый итератор, который должен использоваться в вашем случае для поддержанияwhile
правильной работы цикла.Ответы:
Ключевые значения элементов в a
std::set
являютсяconst
веской причиной. Изменение их может разрушить порядок, который необходим дляstd::set
.Следовательно, решение состоит в том, чтобы стереть итератор и вставить новый с ключом
*it - sub
. Обратите внимание, чтоstd::set::erase()
возвращается новый итератор, который должен использоваться в вашем случае, чтобы цикл while работал правильно.Вывод:
Живая Демо на Колиру
Изменения во
std::set
время итерации по нему не являются проблемой в целом, но могут вызвать тонкие проблемы.Наиболее важным фактом является то, что все используемые итераторы должны быть сохранены или могут больше не использоваться. (Вот почему текущему итератору элемента стирания присваивается возвращаемое значение,
std::set::erase()
которое является либо неповрежденным итератором, либо концом набора.)Конечно, элементы могут быть вставлены также за текущим итератором. Хотя это не проблема,
std::set
это может нарушить цикл моего примера выше.Чтобы продемонстрировать это, я немного изменил приведенный выше пример. Пожалуйста, обратите внимание, что я добавил дополнительный счетчик для предоставления завершения цикла:
Вывод:
Живая Демо на Колиру
источник
std::set
. Возможно, потребуется рассмотреть граничный случай, когда новый итератор вставляется непосредственно за стертым. - Он будет пропущен после вставки в цикл.extract
узлы, изменять их ключи и возвращать их обратно в set. Это было бы более эффективно, поскольку позволяет избежать ненужных распределений.std::set
. Поскольку вы не можете иметь один и тот же элемент дважды, вставка просто оставитstd::set
неизменным, и вы потеряете элемент позже. Рассмотрим, например, входной набор:{10, 20, 30}
сadd = 10
.Просто заменить его другим набором
источник
Вы не можете мутировать элементы
std::set
дизайна. Видетьhttps://en.cppreference.com/w/cpp/container/set/begin
Это потому, что набор отсортирован . Если вы изменяете элемент в отсортированной коллекции, коллекция должна быть отсортирована снова, что, конечно, возможно, но не в C ++.
Ваши варианты:
std::set
, измените его, затем вставьте снова. (Это не очень хорошая идея, если вы хотите изменить каждый элемент)источник
A
std::set
обычно реализуется как самобалансирующееся двоичное дерево в STL.*it
значение элемента, используемого для упорядочения дерева. Если бы было возможно изменить его, заказ стал бы недействительным, следовательно, это невозможно сделать.Если вы хотите обновить элемент, то вы должны найти этот элемент в наборе, удалить его и вставить обновленное значение элемента. Но так как вам нужно обновить значения всех элементов, то вы должны стереть и вставить все элементы по одному.
Это можно сделать за один цикл
sub > 0
.S.erase(pos)
удаляет итератор в позицииpos
и возвращает следующую позицию. Еслиsub > 0
обновленное значение, которое вы вставите, будет предшествовать значению в новом итераторе в дереве, но еслиsub <= 0
, тогда обновленное значение будет следовать после значения в новом итераторе в дереве, и, следовательно, вы получите бесконечный цикл.источник
Ошибка в значительной степени объясняет проблему
Члены
std::set
контейнера естьconst
. Изменение их делает их соответствующий порядок недействительным.Для изменения элементов
std::set
вам нужно будет стереть элемент и повторно вставить его после его изменения.В качестве альтернативы, вы можете использовать,
std::map
чтобы преодолеть этот сценарий.источник