I started out by using a std::string
as my map key, as each item in my map can be uniquely identified by a string alone.
Then I realised that it would be a lot more useful to me to have the map ordered in a certain way, based on another parameter, so I added an int
called priority to my key to help with ordering. The idea is that I iterate over the map and process the higher priority items first. I now have the following user-defined struct
as my map key :
struct MyKey {
// key data
std::string addr;
int priority;
// constructor
MyKey(const std::string & s, const int p)
: addr(s), priority(p) {}
// overloaded operator
bool operator<(const MyKey &that) const {
// same key if addr is the same
if (that->addr == this.addr)
return false;
// not same key so look at priorities to determine order
if (that.priority < this->priority)
return true;
if (that.priority > this->priority)
return false;
// priorities are the same so use the string compare
return (that.addr > this->addr);
}
};
The map ordering appears to be working correctly, and when new items are added they are entered at the expected position automatically if you were to iterate over the map. For instance for a map of std::string
values:
std::map<myKey, std::string> myMap;
myKey key1 = myKey(std::string("key1"), 1);
myKey key2 = myKey(std::string("key2"), 2);
myKey key3 = myKey(std::string("key3"), 3);
myKey key4 = myKey(std::string("key4"), 4);
myMap[key1] = std::string("value1");
myMap[key2] = std::string("value2");
myMap[key3] = std::string("value3");
myMap[key4] = std::string("value4");
Would result in the following map key-value pairs at respective indexes:
[0] { addr = "key4", priority = 4 }, { "value4" }
[1] { addr = "key3", priority = 3 }, { "value3" }
[2] { addr = "key2", priority = 2 }, { "value2" }
[3] { addr = "key1", priority = 1 }, { "value1" }
However...I am having problems when it comes to modifying an existing priority of a key that is already present in the map.
In this situation, find()
and []
(with respect to std::map
) don't work as I want them to:
myKey modified_key1 = myKey(std::string("key1"), 5);
// problem 1 - this does not return iterator to "key1",
// but instead to end of the map
auto & foundKey = myMap.find(modified_key1);
// problem 2 - this adds a brand new item to the map
myMap[modified_key1] = std::string("value1");
After problem 2
as mentioned above, I am getting a new item added to the map with the same addr
of an existing item. The new item appears to be added in the expected position based on the new (modified) priority
, but the existing item to be updated remains as it was. So I end up with 2 items in the map with the same addr
in their keys:
[0] { addr = "key1", priority = 5 }, { "value1" }
[1] { addr = "key4", priority = 4 }, { "value4" }
[2] { addr = "key3", priority = 3 }, { "value3" }
[3] { addr = "key2", priority = 2 }, { "value2" }
[4] { addr = "key1", priority = 1 }, { "value1" }
This is a problem for me as I would like to still rely on the notion that the addr
of the map item key is unique.
What I want is the map to realise it already has an item with the same key (or more to the point the same key addr
) and to re-order the item accordingly.
I have tried experimenting with compare functors as part of the map definition, and also overloading the keys ==
operator, but the same problem persists.
What am I missing or should I be approaching this differently?
The problem is that your comparison operator implemented incorrectly, it does not provide strict weak ordering hence undefined behavior of the std::map
, lets say you have 3 objects of MyKey
:
MyKey mk1{ "a",3 }, mk2{ "b", 2 }, mk3 { "a", 1 };
mk1 < mk2 -> true as 3 > 2
mk2 < mk3 -> true as 2 > 1
mk1 < mk3 -> false as addr is the same, but must be true
I do not think your problem is easily solvable with std::map
. Possible solution is to use boost::multi_index
with address as one index and priority as another. To change priority of existing element boost::multi_index
provides method to replace data.