Reputation: 739
I need to have a set of custom structs in order to be able to quickly retrive the instance with the smallest given parameter. However, I discovered that std::set considers some instances the same, even though they have different values. Here is my example program:
#include <set>
#include <iostream>
struct S
{
int foo, bar;
S(int foo, int bar): foo(foo), bar(bar) {}
};
inline bool operator<(const S& a, const S& b)
{
return a.foo < b.foo;
}
int main()
{
std::set<S> baz;
baz.emplace(1, 2);
baz.emplace(1, 3);
std::cout << baz.size();
return 0;
}
This program prints 1
std::set considers S(1, 2)
and S(1, 3)
to be the same. I'm guessing this is because bar
is not used when comparing them. But I need the set to keep both elements, how do I solve this?
EDIT: I feel I have not stated my question correctly: I want to keep instances that are not exactly the same, but std::multiset
does not work for me, because I don't want identical instances to be in the container
SOLUTION: I think I understand what was wrong. I assumed that if for 2 elements a < b
and b < a
were both true, it would result in undefined behaviour. But std::set checks for this, so it removes one of the elements. The best solution for me is to modify the comparator so it includes bar
.
Upvotes: 2
Views: 383
Reputation: 70411
It's not std::set
that considers instances of S
the same if they have the same foo
, it's your own operator<
.
So you either have to:
operator<
(which would also affect sorts), oroperator<
to be fit for std::set
and use a different Compare
parameter for std::sort
(or however you get the smallest element).Bottom line, if you want a different compare functionality for std::sort
(or whatever) and std::set
, you need to provide different functionality.
As you did not really specify why your operator<
looks the way it does (not considering S::bar
), it's hard to tell what would match your intentions.
Upvotes: 3
Reputation: 16156
Perhaps as addition to the other answers:
std::set
keeps a sorted set, and uses the <
-relation created by the operator<
to sort the elements.
So given two objects a
and b
... if ! (a < b)
and ! (b < a)
, then neither is a
"less" than b
, nor is b
"less" than a
. Therefore a == b
- from the perspective of the sorted order however.
Upvotes: 1
Reputation: 22394
Simplifying a bit, std::set
compares two elements twice when inserting:
a < b
b < a
If 1. is true
, then a
goes before b
in ordering.
Else if 2. is true
, then b
goes before a
.
Else (both are false
), a
and b
are equivalent.
Since S{1, 2} < S{1, 3} == false
and S{1, 3} < S{1, 2} == false
according to your definition of operator <
, they are deemed equivalent and std::set::emplace
fails.
Upvotes: 2
Reputation: 61
You can try to use std::multiset
http://www.cplusplus.com/reference/set/multiset/
Upvotes: 0
Reputation: 30005
This happens because you only compare foo
, and if foo
is equivalent, the objects are considered equivalent. std::set
keeps only one of equivalent values. If bar
makes objects unique, include bar
in the comparison or else, if you still want to keep both (equivalent) values, use a std::multiset
. Both are valid solutions depending on what you want to do.
Upvotes: 3
Reputation: 10103
If both foo
and bar
make S
unique, then you can modify your operator<
to use both variables, like so:
inline bool operator<(const S& a, const S& b)
{
if ( a.foo == b.foo )
return a.bar < b.bar;
return a.foo < b.foo;
}
Upvotes: 1