Search code examples
c++hashmapc++17shared-ptr

c++ change object properties of object that is key in map?


Say I have the following classes:

#include <string>

class Item {
public: 
   Item(std::string name, int id);
   virtual int getWeight() = 0;

protected:
   std::string name;
   const int id;
}
#include <vector>
#include <memory>
#include "Item.h"

class Bucket : Item { 
public:
    Bucket(std::string name, std::string type, int id) // In the implementation this will call the Constructor of Item
    bool operator<(const Bucket& b) const {
        return (id < b.id );
    }
    void addItem(std::shared_ptr<Item> itemm) {
      this->weight += item->getWeight();
      this->items.push_back(item);
    }
    int getWeight() override; //this implementation does not matter to the problem

private:
    std::string type;
    std::vector<std::shared_ptr<Item>> items;
    int weight = 0;

}

There are other classes inheriting from class Item aswell, but to make it easier I am only showing the class Bucket.

Now in my main method, I want to iterate over a map, that already contains some entries and call a method that changes one property of the key object. main.cpp:

#include <map>
#include <memory>
#include <vector>
#include "Item.h"
#include "Bucket.h"

using namespace std;

int main(){
   map<Bucket, vector<shared_ptr<Item>>> map; // Imagine this map has some entries
   for(auto itr = map.begin(); itr != map.end(); itr++){
        for_each(itr->second.begin(), itr->second.begin(), [&itr](shared_ptr<Item> item){
            itr->first.addItem(item); // This does not compile, the error will be in the text below
        });

}

As told in the code, it does not compile with the following error on line itr->first.addItem(item);: 'this' argument to member function 'addItem' has type 'const Bucket', but function is not marked const.

I can't make the method const, since it is changeing some property of the object and I would get an error there. I am not sure if I understand this error correctly, but I think the problem is, that as I put a Bucket into a map (Bucket is the key), it becomes const. If that is the case, is there a way to tell the compiler to use only the id property of Bucket as the const key of the map (Without changeing the map to something like map<int, <pair<Bucket,vector<shared_ptr<Item>>>>>)? Or am I not understanding the main problem here?


Solution

  • I think the problem is, that as I put a Bucket into a map (Bucket is the key), it becomes const

    Correct. Maps are supposed to be ordered, and if you were able to mutate a key, you could break this invariant. Doing this would potentially break every subsequent search or insert horribly.

    The linked workaround is effective, but ugly. Unless map-key-ness is a core feature of your Bucket (I can't tell from the sample code whether that's likely), making some of its members mutable feels like a hack.

    Your whole design looks odd, tbh - you're going to end up with a map full of Bucket keys that duplicate the information in the second half of the key,value pair. Are you going to move those Buckets somewhere else afterwards, or are they going to live forever shackled to those vestigial vectors of redundant references?

    If that map is an intermediate step in building up connections, it shouldn't be where the Bucket objects live. Perhaps you should have one main lookup map<id, Item>, another transient mapping multimap<id, id> describing which Item contains which other Items.