I have some instances of a class allocated in an unordered_map. I also have different containers that store pointers to these elements with different orderings. Which means that for example, I have a std::set of pointers to the elements allocated in the map, ordered by a subset of the instances' fields.
Since I have access to the real elements, I can change their fields, but I know that I shouldn't do that on the fields that are used in the ordering of the set. In fact, what I need to do before changing those fields is to delete the object's pointer from the set, change those fields and insert it back again, like this:
set<Element*, Comparator> s; // Elements ordered by field_2
s.erase(element);
element->field_2 = 4;
s.insert(element);
However, the other containers that maintain different orderings are implemented by myself, and I know that I can change those values and then notify the container that the fields have been updated. So I'm wondering if I can change the order of those instructions to this one:
element->field_2 = 4;
s.erase(element);
s.insert(element);
The reason I want to do it this way is that I want all those containers to share the same interface. So ideally I would like to change the fields and then call the container's method container.value_updated(element).
So, can I modify the critical field_2 and then immediately call delete & insert? Or the deletion will fail since the value of field_2 can be inconsistent? (I think this will be implementation dependent but I want to make sure)
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