Search code examples
c++vectorkeystdmapmultikey

Appending map that uses Struct as a "multikey" and std::vector as mapped value


in code below it appends myList only for the first call of appendMyList(), and size stays 1, so can someone explain what is wrong here:

struct MyKey {
  int accValue;
  std::string name;
};

inline bool operator<(MyKey a, MyKey b){
  return a.accValue < b.accValue && a.name < b.name;
}

inline bool operator==(MyKey a, MyKey b){
  return a.accValue == b.accValue && a.name == b.name;
}

typedef std::map<MyKey, std::vector<int>> MyList;

class MyClass {
    public:
    void appendMyList(int accVal, std::string name, int element) {
        myList[MyKey{accVal, name}].push_back(element);
        cout<<endl<<"Size after"<< myList.size()<<endl;
    }

   MyList myList;
};

I saw similar post here but don't see anything wrong with my operators, so I guess it's something else?

This is how i call the function:

int main()
{
    MyClass imHere;
    int a = 1;
    std::string yaya = "name";
    for (int i = 0; i<10; i++) {
            imHere.appendMyList((++a), yaya, i);
    }

    return 0;
};

Solution

  • std::map does not allow for storing duplicates. However, it doesn't use operator== for checking equality of elements. It uses operator< for both -- ordering elements as well as checking equivalence.

    That is, if !(a < b) && !(b < a) then std::map considers a and b equivalent, hence no more elements than one are inserted using your operator<, because name is the same in all elements, thus both (a < b) and (b < a) return false.

    Instead, you should be using:

    inline bool operator<(MyKey a, MyKey b) {
      return a.accValue < b.accValue || (a.accValue == b.accValue && a.name < b.name);
    }
    

    or:

    inline bool operator<(MyKey a, MyKey b) {
      return std::tie(a.accValue, a.name) < std::tie(b.accValue, b.name);
    }