Oto mój kod:
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++;
}
Nie mogę zaktualizować wartości ustawionej przez iterator. Chcę odjąć wartość całkowitą „sub” od wszystkich elementów zestawu.
Czy ktoś może mi pomóc, gdzie jest rzeczywisty problem i jakie byłoby faktyczne rozwiązanie?
Oto komunikat o błędzie:
error: assignment of read-only location ‘it.std::_Rb_tree_const_iterator<int>::operator*()’
28 | *it=*it-sub;
| ~~~^~~~~~~~
*it - sub
. Pamiętaj, żestd::set::erase()
zwraca nowy iterator, który musi być użyty w twoim przypadku, abywhile
pętla działała poprawnie.Odpowiedzi:
Kluczowe wartości elementów w a
std::set
sąconst
nie bez powodu. Modyfikowanie ich może zniszczyć porządek, który jest niezbędny dlastd::set
.Dlatego rozwiązaniem jest usunięcie iteratora i wstawienie nowego z kluczem
*it - sub
. Pamiętaj, żestd::set::erase()
zwraca nowy iterator, który musi być użyty w twoim przypadku, aby pętla while działała poprawnie.Wynik:
Demo na żywo na coliru
Zmiany w
std::set
trakcie iteracji nie są ogólnie problemem, ale mogą powodować subtelne problemy.Najważniejsze jest to, że wszystkie używane iteratory muszą pozostać nienaruszone lub nie można ich już używać. (Dlatego właśnie bieżącemu iteratorowi elementu kasującego przypisuje się wartość zwracaną,
std::set::erase()
która jest albo nienaruszonym iteratorem, albo końcem zestawu).Oczywiście elementy można wstawić również za bieżącym iteratorem. Chociaż nie stanowi to problemu
std::set
, może przerwać pętlę mojego powyższego przykładu.Aby to zademonstrować, zmieniłem nieco powyższą próbkę. Pamiętaj, że dodałem dodatkowy licznik, aby umożliwić zakończenie pętli:
Wynik:
Demo na żywo na coliru
źródło
std::set
. Konieczne może być rozważenie przypadku granicznego, że nowy iterator jest wstawiany bezpośrednio za skasowanym. - Zostanie pominięty po wstawieniu do pętli.extract
węzły, modyfikować ich klucze i przywracać je z powrotem do zestawu. Byłoby to bardziej wydajne, ponieważ pozwala uniknąć niepotrzebnych przydziałów.std::set
. Ponieważ nie możesz mieć tego samego elementu dwa razy, wstawienie pozostawistd::set
niezmienione, a element stracisz później. Rozważmy na przykład zestaw danych wejściowych: za{10, 20, 30}
pomocąadd = 10
.Po prostu zastąp go innym zestawem
źródło
Nie można mutować elementów
std::set
według projektu. Widziećhttps://en.cppreference.com/w/cpp/container/set/begin
To dlatego, że zestaw jest posortowany . Jeśli mutujesz element w posortowanej kolekcji, kolekcja musi zostać ponownie posortowana, co oczywiście jest możliwe, ale nie w języku C ++.
Twoje opcje to:
std::set
, zmodyfikuj go, a następnie wstaw ponownie. (Nie jest dobrym pomysłem, jeśli chcesz zmodyfikować każdy element)źródło
A
std::set
jest zwykle implementowane jako samo-równoważące się drzewo binarne w STL.*it
jest wartością elementu używanego do uporządkowania drzewa. Gdyby można było go zmodyfikować, zamówienie stałoby się nieważne, dlatego nie można tego zrobić.Jeśli chcesz zaktualizować element, musisz znaleźć ten element w zestawie, usunąć go i wstawić zaktualizowaną wartość elementu. Ale ponieważ musisz zaktualizować wartości wszystkich elementów, musisz usunąć i wstawić wszystkie elementy jeden po drugim.
Można to zrobić w jednym z dostępnych pętli
sub > 0
.S.erase(pos)
usuwa iterator w pozycjipos
i zwraca następującą pozycję. Jeślisub > 0
zaktualizowana wartość, którą wstawisz, pojawi się przed wartością w nowym iteratorze w drzewie, ale jeślisub <= 0
, to zaktualizowana wartość pojawi się po wartości w nowym iteratorze w drzewie, a zatem skończysz na nieskończona pętla.źródło
Błąd właściwie wyjaśnia problem
Członkami
std::set
kontenera sąconst
. Ich zmiana powoduje, że ich zamówienie jest nieważne.Aby zmienić elementy
std::set
, będziesz musiał usunąć element i włożyć go ponownie po zmianie.Alternatywnie możesz użyć,
std::map
aby pokonać ten scenariusz.źródło