Consider the following data structures and code.
struct Sentence {
std::string words;
int frequency;
Sentence(std::string words, int frequency) : words(words), frequency(frequency) {}
};
struct SentencePCompare {
bool operator() (const Sentence* lhs, const Sentence* rhs) const {
if (lhs->frequency != rhs->frequency) {
return lhs->frequency > rhs->frequency;
}
return lhs->words.compare(rhs->words) < 0;
}
};
std::set<Sentence*, SentencePCompare> sentencesByFrequency;
int main(){
Sentence* foo = new Sentence("foo", 1);
Sentence* bar = new Sentence("bar", 2);
sentencesByFrequency.insert(foo);
sentencesByFrequency.insert(bar);
for (Sentence* sp : sentencesByFrequency) {
std::cout << sp->words << std::endl;
}
foo->frequency = 5;
for (Sentence* sp : sentencesByFrequency) {
std::cout << sp->words << std::endl;
}
}
The output of the above code is the following.
bar
foo
bar
foo
As we might expect, when an object pointed to by the pointer in the set is updated, the set does not automatically re-evaluate the predicate, even though the predicate orders the pointers based on the objects they point at.
Is there a way to force the std::set
to re-evaluate the predicates, so that the order is correct again?
No.
There's a reason why set
only allows const
access to its elements. If you sneak past that by using shallow-const pointers and custom predicates and then destroy the invariant by modifying the pointee in a way that affects ordering, you'll pay the price in the form of nasal demons.
Before C++17, you need to erase
and insert
again, which incurs a key copy plus node deallocation and allocation. After, you can extract
the node, modify it, and reinsert it, which is free.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With