Cant insert element into map

Ive been playing around with maps and I decided to write a small program to test it out and ive gotten stuck because i cannot insert any elements into the map in my class. The error is:

Severity Code Description Project File Line Suppression State
Error C2676 binary '<': 'const _Ty' does not define this operator or a conversion to a type acceptable to the predefined operator

This works when i write a progrma in main. Also using insert(pair<Item, int>(item, amount)) doesnt work either. The problem is on line 96, if i comment it out the code compiles.
Using Visual Studio 2022


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <iostream>
#include <string>
#include <map>

using namespace std;

class Item
{
public:
    Item
    (
        const string& name,
        const int stackSize,
        const float weight,
        const int storageSlotsUsed,
        const string& description
    ) : mName(name),
        mStackSize(stackSize),
        mWeight(weight),
        mStorageSlotsUsed(storageSlotsUsed),
        mDescription(description)
    {}

    string GetName() const { return mName; }
    int GetStackSize() const { return mStackSize; }
    float GetWeight() const { return mWeight; }
    string GetDescription() const { return mDescription; }

private:
    string mName{ "Item Name" };
    const int mStackSize{ 0 };
    const float mWeight{ 0 };
    const int mStorageSlotsUsed{ 0 };
    string mDescription{ "Item Description" };
};

class Weapon : public Item
{
public:
    Weapon
    (
        const string& name,
        const int stackSize,
        const float weight,
        const int storageSlotsUsed,
        float attackDamage,
        const string& description
    ) :
        Item{ name, stackSize, weight, storageSlotsUsed, description },
        mAttackDamage(attackDamage)
    {}

    float GetDamage() const { return mAttackDamage; }

private:
    float mAttackDamage{ 0 };
    const int mStaminaUse{ 0 };
};

void Output(Item& item)
{
    cout << item.GetName() << '\n';
    cout << item.GetDescription() << '\n';
    cout << "Weight: " << item.GetWeight() << " lbs" << '\n';
    cout << "Max Stack Size: " << item.GetStackSize() << "\n\n";
}

class Player
{
public:
    Player(const string& name, int stamina) : mName(name), mStamina(stamina)
    {}

private:
    string mName{ "Player" };
    int mStamina{ 100 };
};

void Output(Weapon& weapon)
{
    cout << weapon.GetName() << '\n';
    cout << weapon.GetDescription() << '\n';
    cout << "Damage: " << weapon.GetDamage() << '\n';
    cout << "Weight: " << weapon.GetWeight() << " lbs" << '\n';
    cout << "Max Stack Size: " << weapon.GetStackSize() << "\n\n";
}

class Container
{
public:
    Container(const string& name, int storageSlots) : mName(name), mStorageSlots(storageSlots)
    {}

    void Add(Item& item, int amount)
    {
        mContainer.insert({ item, amount });
    }

    void Remove()
    {

    }

    int GetSlotsUsedCount() const
    {
        return mStorageSlots;
    }

    string GetName() const { return mName; }

private:
    string mName{ "Container Name" };
    int mStorageSlots{ 0 };
    map<Item, int> mContainer;
};

int main()
{
    Player Hero("Hero", 100);
    Container PlayerInventory("Player Inventory", 100);

    Item GlowLeaf("Glow Leaf", 99, 0.1, 1, "A glowing leaf that blooms at night.");
    Item WeaponRepairKit("Weapon Repair Kit", 99, 0.4, 2, "Repairs any weapon.");
    Item GlowRock("Glow Rock", 99, 0.8, 1, "A strange rock that emits a continuous glow.");
    Weapon Katana("Katana", 1, 3.1, 12, 65, "A blade once wielded by Samurai of ancient Japan.");

    Output(GlowLeaf);
    Output(WeaponRepairKit);
    Output(GlowRock);

    Output(Katana);

    return 0;
}
Last edited on
A map requires an order relation for its keys, because it sorts itself.

Your key is of type Item, but no ordering < is defined for such objects.
I think I understand, So i have to overload the < operator?
that or use unordered map.
You don't need to define operator < for your class. It might not make sense or there might be many different ways you want to compare your class.

What you could do instead is to define a struct that overloads operator(), takes two Item objects as argument and returns a bool. Similar to the < operator, it should return true if the first argument should be ordered before the second argument otherwise it should return false. It is very important that you get this right. It has to be consistent.

This is what such a "comparator type" could look like:
1
2
3
4
5
6
7
struct ItemCmp
{
	bool operator()(const Item& item1, const Item& item2) const
	{
		return item1.GetName() < item2.GetName();
	}
};

And then you pass this "comparator type" as the third template argument to std::map:
 
map<Item, int, ItemCmp> mContainer;

This will treat all items with the same name as being identical but you can change this by changing the implementation of ItemCmp::operator().
Last edited on
As jonnin mentioned, I think the OP will be better off with std::unordered_map

It looks like Container is a bag with stuff in it; probably won't have lots of Items (tens never mind millions); not going to output sorted; etc.
If you decide to use std::unordered_map you instead need to provide equal comparison and a hash function which is not necessarily easier but it might be more efficient (sometimes).
Also note that std::unordered_map allows duplicate keys, whereas std::map doesn't allow duplicate keys.

That's std::unordered_multimap (and std::multimap for map with duplicate keys).

Each element is inserted only if its key is not equivalent to the key of any other element already in the container (keys in an unordered_map are unique).


http://www.cplusplus.com/reference/unordered_map/unordered_map/insert/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <string>
#include <map>

struct Item
{
    std::string mName{ "Item Name" };
    int mStackSize{ 0 };
    float mWeight{ 0 };
    int mStorageSlotsUsed{ 0 };
    std::string mDescription{ "Item Description" };
    
    Item(){};
    
    Item(std::string name, int size, float weight, int slots, std::string descr)
    {
        mName = name;
        mStackSize = size;
        mWeight = weight;
        mStorageSlotsUsed = slots;
        mDescription = descr;
    }
};

int main()
{
    Item GlowLeaf("Glow Leaf",99,0.1,1,"A glowing leaf that blooms at night.");
    Item WeaponRepairKit("Weapon Repair Kit",99,0.4,2,"Repairs any weapon.");
    Item GlowRock("Glow Rock",99, 0.8, 1, "A strange rock that emits a glow.");
    
    std::map<std::string, Item> store;
    
    store[GlowLeaf.mName] = GlowLeaf;
    store[WeaponRepairKit.mName] = WeaponRepairKit;
    store[GlowRock.mName] = GlowRock;
    
    for(auto i: store)
        std::cout
        << "Index: " << i.first
        << " Detail: " << i.second.mName << ',' << i.second.mDescription << '\n';
    
    return 0;
}


Index: Glow Leaf Detail: Glow Leaf,A glowing leaf that blooms at night.
Index: Glow Rock Detail: Glow Rock,A strange rock that emits a glow.
Index: Weapon Repair Kit Detail: Weapon Repair Kit,Repairs any weapon.
Program ended with exit code: 0
Hmm, I’m wondering if it would just be easier to stick with a vector 😂. I didnt think it would require this much extra effort to use a map haha
Last edited on
what do you want, really?
every data structure has pros and cons. You can find online a flow chart to help choose but if you have had any data structures study you will know it pretty well off the top of your head, just list out the requirements of the container and what you want it to be good at, and what you can let slide (eg does it need to be sorted, do you insert at the end or the middle, do you change it frequently or rarely, ... blah blah). With the requirement, the choice becomes clear. Don't be afraid of a little work to use the built in containers, they are very strong tools and worth a few days going over how to use them and what their capabilities are.

Using a vector for everything is easier. It is also wrong, in many cases. Its right more often than not, but some 15% of the time, its going to be woefully inefficient or have some drawback that makes it the wrong choice (even if it works).
Last edited on
Ch1156 wrote:
Ive been playing around with maps

Excellent idea. Learn to use what C++ has to offer so when a container is needed you understand the advantages AND disadvantages of each for the task at hand.

Which C++ container is used depends on what the primary use of the container and its contents will be.

https://en.cppreference.com/w/cpp/container

The two main types of containers are Sequential and Associative.

Sequential doesn't mean the contents are contiguous, though they can be. Accessing the elements in a sequential manner is the main advantage of these containers.

If the container's contents are to be searched a lot, then an associative container might be more useful. Searches tend to be as quick or quicker than with sequential containers.

Ch1156 wrote:
Hmm, I’m wondering if it would just be easier to stick with a vector

Vectors are a good generic container for many purposes, a nice replacement for old style arrays. Accessing the contents are very similar, so updating old code that uses arrays doesn't require a massive rewrite of lines and lines of code other than initialization.

Lists/forward lists are also decent generic containers.

Using regular arrays that are sized/resized at run-time can be a manual memory management nightmare. Using a vector relieves the coder of that potential mess.
The examples in the C++ container section here at CPlusPlus have sample code of each container you might want to look and and muck around with, including how to insert elements into the container.

http://www.cplusplus.com/reference/stl/
Ch1156 wrote:
I didnt think it would require this much extra effort to use a map haha


againtry's last example is pretty simple.

Another container worth looking into is std::deque

As others have said, evaluate each container for what you want it to do.

Another thing to consider is how much data one has: if it's small then it won't make much difference in terms of time as to what container one uses, as long as the algorithms being used are not too horrible. Ease of coding becomes a bigger factor then, IMO. To make a difference in timing, one usually has to have at least 1 million data items - that's where I start if I am testing some code for speed.
I didnt think it would require this much extra effort to use a map haha


I'm surprised the couple of extra lines involved constitute 'extra effort'. It's a sample whereby most of the code is cut and paste from @OP's stuff.

Labouring through all the (desparate) reasons why it can't be done is far more effort than actually doing it.

Here's a struct version to show how straightforward it is. Pls keep in mind I don't really care whether a <map> is used or not.

Using a <vector> is OK too - not much different in terms of workload.

The world might be a better place if it is or is not, but I'm sure we'll survive the trauma of that decision by @OP.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <iostream>
#include <string>
#include <map>

struct Item
{
    std::string mName{ "Item Name" };
    int mStackSize{ 0 };
    float mWeight{ 0 };
    int mStorageSlotsUsed{ 0 };
    std::string mDescription{ "Item Description" };
    
    Item(){};
    
    Item(std::string name, int size, float weight, int slots, std::string descr)
    {
        mName = name;
        mStackSize = size;
        mWeight = weight;
        mStorageSlotsUsed = slots;
        mDescription = descr;
    }
};

typedef std::map<std::string, Item> Map;
typedef std::pair<std::string, Item> Pair;
typedef std::pair<std::map<std::string, Item>::iterator,bool> Iterator;

struct Container
{
    Map m_container;
    
    void add(Item item)
    {
        Iterator iter = m_container.insert( Pair(item.mName, item) );
        
        if(iter.second == false)
            std::cout << " *** There's one already. Re-name!\n";
        
    }
    
    void print()
    {
        for(auto i: m_container)
        std::cout
        << "Index: " << i.first
        << " Detail: " << i.second.mName << ',' << i.second.mDescription << '\n';
    }
};

int main()
{
    Item GlowLeaf("Glow Leaf",99,0.1,1,"A glowing leaf that blooms at night.");
    Item WeaponRepairKit("Weapon Repair Kit",99,0.4,2,"Repairs any weapon.");
    Item GlowRock("Glow Rock",99, 0.8, 1, "A strange rock that emits a glow.");
    
    Container store;
    
    store.add(GlowLeaf);
    store.add(GlowRock);
    store.add(WeaponRepairKit);
    store.add(GlowRock);
    
    store.print();
    
    return 0;
}


*** There's one already. Re-name!
Index: Glow Leaf Detail: Glow Leaf,A glowing leaf that blooms at night.
Index: Glow Rock Detail: Glow Rock,A strange rock that emits a glow.
Index: Weapon Repair Kit Detail: Weapon Repair Kit,Repairs any weapon.
Program ended with exit code: 0
Last edited on
@Ch1156,

If you just put the line
bool operator < ( const Item &other ) const { return mName < other.mName; }
after your line 27 (say) then you can go ahead and use your map.
Exactly!
And that's the reason for the ...
Iterator iter = m_container.insert( Pair(item.mName, item) );
Topic archived. No new replies allowed.