How to update an existing element of std::set?
Since val
is not involved in comparison, it could be declared mutable
struct Foo {
Foo(int i, int j) : id(i), val(j) {}
int id;
mutable int val;
bool operator<(const Foo& other) const {
return id < other.id;
}
};
This implies that the value of val
may change in a logically-const Foo, which means that it shouldn't affect other comparison operators etc.
Or you could just remove and insert, that takes O(1) additional time (compared to accessing and modifying) if insertion uses the position just before just after the old one as the hint.
Something like:
bool alreadyThere = !p.second; // you forgot the !
if (alreadyThere)
{
Set::iterator hint = p.first;
hint++;
s.erase(p.first);
s.insert(hint, f);
}
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
C++ std::set update is tedious: I can't change an element in place
set
returns const_iterators
(the standard says set<T>::iterator
is const
, and that set<T>::const_iterator
and set<T>::iterator
may in fact be the same type - see 23.2.4/6 in n3000.pdf) because it is an ordered container. If it returned a regular iterator
, you'd be allowed to change the items value out from under the container, potentially altering the ordering.
Your solution is the idiomatic way to alter items in a set
.
How can i replace element in set?
You have got to erase()
one element and insert()
the new one. replace
is not defined for std::set
. See http://www.cplusplus.com/reference/set/set/
Update element in a set of pointer of elements before deletion
Data which is used as key in a std::set
may not be changed. Otherwise, the order of std::set
is broken and addressing elements in the std::set
cannot work anymore.
Out of curiosity, I tried to do it the wrong way.
Although I know it is undefined behavior, I got this running – a nice demonstration that bad things happen:
#include <iostream>
#include <set>
#include <vector>
typedef int Entry;
struct Less {
bool operator()(const Entry *p1, const Entry *p2) const
{
return *p1 < *p2;
}
};
int main()
{
const int N = 10;
std::vector<int> data;
for (int i = 0; i < N; ++i) data.push_back(i);
std::set<Entry*, Less> set;
for (int &value : data) set.insert(&value);
// do it wrong
data[2] = 12;
set.erase(&data[2]);
set.insert(&data[2]);
// check result
for (const Entry *pValue : set) std::cout << ' ' << *pValue;
std::cout << '\n';
// done
return 0;
}
Output:
0 1 12 3 4 5 6 7 8 9 12
Live Demo on coliru
How to change a set element?
The elements of the set
will be in sorted order. If you are allowed to modify an element, then this sorting order can not be maintained. Hence you can not modify the item. You need to erase the existing element and insert a new one.
How to update std::map after using the find method?
std::map::find
returns an iterator to the found element (or to the end()
if the element was not found). So long as the map
is not const, you can modify the element pointed to by the iterator:
std::map<char, int> m;
m.insert(std::make_pair('c', 0)); // c is for cookie
std::map<char, int>::iterator it = m.find('c');
if (it != m.end())
it->second = 42;
How to update recently entered element in std::vector pair int, int ?
Assuming that you want to update the last std::pair
input just after inserting to the std::vector<std::pair<int, int>>
.
In c++17 you can make use of second overload of std::vector::emplace_back
, which returns a referance to the element inserted:
#include <vector>
std::vector<std::pair<int, int>> vec;
auto &pair = vec.emplace_back(1, 3); // construct in-place and get the reference to the inserted element
pair.second = 5; // change the value directly like this
Update:
In c++11, the same can be achieved by the std::vector::insert
member, which returns iterator pointing to the inserted element.
#include <vector>
std::vector<std::pair<int, int>> vec;
// insert the element to vec and get the iterator pointing to the element
const auto iter = vec.insert(vec.cend(), { 1, 3 });
iter->second = 5; // change the value
Related Topics
Memory Stability of a C++ Application in Linux
How to Know If One Shared Library Depends on Another Shared Library or Not
How to Make a Function Async-Signal-Safe
Libstdc++ Static Linking in Dynamic Library
/Usr/Lib64/Libstdc++.So.6: Version 'Glibcxx_3.4.15' Not Found
How to Get a List of Installed True Type Fonts on Linux Using C or C++
Why Can't I Add a Qgridlayout to a Qmainwindow
Why Isn't Malloc Filling Up Memory
Determine If Linux or Windows in C++
Get Computer Name and Logged User Name
How to Force Linker to Use Shared Library Instead of Static Library
Using Qsocketnotifier to Select on a Char Device
How to Compile SQLite with Icu