objects as key of map: why repetition being allowed?


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
#include <iostream>
#include <map>
using namespace std;

struct T
{
	string s;
	double d;	
	T(string ss="", int dd=0) : s(ss), d(dd) {}	
	bool operator< (const T &a)const 
	{
		if(s<a.s) return 1;
		if(s>a.s) return 0;
		return d<=a.d;
	}	
};

int main()
{
	
	map<T,int> M;
	
	T temp;
	temp.s = "abc";
	temp.d = 4.5;
	
	M.insert(pair<T,int>(T("xyz", 1.2), 1));
	M.insert(pair<T,int>(temp, 1));	
	++M[temp]; // repetition
	++M[temp]; // repetition
	
	cout << M.size(); // 4 
}
Maps only guard against duplicate keys. Duplicate values are perfectly fine (and desirable).

Maybe you want to use std::set instead?
Your comparing function has a flaw:

1
2
3
4
5
6
	bool operator< (const T &a)const 
	{
		if(s<a.s) return 1;
		if(s>a.s) return 0;
		return d<a.d;  // <- must be <, not  <=
	}


If you fix that, lines 29,30 will not cause duplicates any more.
LB wrote:
Maps only guard against duplicate keys. Duplicate values are perfectly fine (and desirable).

Maybe you want to use std::set instead?


i need to insert many objects, map will keep unique keys. if it was previously inserted, it will increase the int(second)
++M[temp];
but in my previous code temp key is inserted 3 times :(

i need object counter alike the word counter in the following example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <string>
#include <iostream>
#include <vector>
#include <map>
 
int main()
{
    std::vector<std::string> words = { 
        "this", "sentence", "is", "not", "a", "sentence",
	"this", "sentence", "is", "a", "hoax" 
    };
 
    std::map<std::string, size_t>  word_map;
    for (const auto &w : words) {
        ++word_map[w];
    }
 
    for (const auto &pair : word_map) {
        std::cout << pair.second
	          << " occurrences of word '"
	          << pair.first << "'\n";
    }
}
> but in my previous code temp key is inserted 3 times :(
as Disch pointed out, that's because your comparison function is ill-formed.
temp<temp is true, so the map "wrongfully" thinks that it is another element.
thanks Disch. it works now.
d<a.d makes it map.
and d<=a.d makes it multimap!

When all your class data members implement operator<, do not write the comparison function yourself - let the standard library handle it for you:
1
2
3
4
5
6
7
8
#include <tuple>

//...

	bool operator<(const T &a) const 
	{
		return std::tie(s, d) < std::tie(a.s, a.d);
	}
tuple comparison looks smarter.
but can i use it if the class has arrays or other class data types ?
Most Standard Library containers (including std::vector in your example) can be compared with operator<. If for some reason you are working with a type that cannot be compared this way, then after using std::tie you can do traditional comparison. You could also implement operator< for the type so that you can stick to just using std::tie.
Topic archived. No new replies allowed.