问题
I have a problem with the operator<() method which is required for a std::map. I'm using a struct as composite key that looks as follows:
struct MyKey {
std::string string1;
std::string string2;
std::string string3;
unsigned int uint1;
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
return mk1.string1 < mk2.string1 && mk1.string2 < mk2.string2 &&
mk1.string3 < mk2.string3 && mk1.uint1 < mk2.uint1;
}
}
As introduced I want to use a composite key with 4 values, but I don't know how to achieve this for the operator< method. I observed that only 1 value is stored at a time!
Can anybody tell me how the right condition looks like?
Thanks in advance!
回答1:
The Standard library's associative containers such as std::map
, std::set
, std::multiset
, std::multimap
, std::bitset
require that the ordering of elements must follow Strict Weak Ordering, which means your implementation of operator<
must follow strict weak ordering. So one implementation could be this:
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
if (mk1.string1 != mk2.string1 )
return mk1.string1 < mk2.string1;
else if ( mk1.string2 != mk2.string2)
return mk1.string2 < mk2.string2;
else if (mk1.string3 != mk2.string3)
return mk1.string3 < mk2.string3;
else
return mk1.uint1 < mk2.uint1;
}
Or you can implement it as:
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
auto const & t1 = std::tie(mk1.string1, mk1.string2, mk1.string3, mk1.uint1);
auto const & t2 = std::tie(mk2.string1, mk2.string2, mk2.string3, mk2.uint1);
return t1 < t2;
}
In this solution, std::tie function creates two tuples t1
and t1
of the references of the arguments passed to it, and then compare t1
and t2
using overloaded operator<
for std::tuple
instead. The operator< for tuple compares the elements lexicographically — strict-weak ordering is achieved..
回答2:
I think you have a problem in that the operator< doesn't necessarily implement strict weak ordering. There are too many combinations where A<B
is false and B<A
is also false, where A
and B
are MyKey
objects. This is interpreted as A
being equal to B
.
回答3:
The problem with your implementation is that it's not stable, consider...
return mk1.string1 < mk2.string1 && mk1.string2 < mk2.string2 &&
mk1.string3 < mk2.string3 && mk1.uint1 < mk2.uint1;
...evaluating { "a", "a", "a", 1 } < { "a", "b", "a", 1 }
= a<a && ...
= false && ...
= false
...but { "a", "b", "a", 1 } < { "a", "a", "a", 1 }
= a<a && ...
= false && ...
= false
So, neither is reported as less than the other, despite them not being equal keys in the map
.
A working solution: it's concise and efficient to do each necessary string comparisons only once...
friend bool operator<(const MyKey& mk1, const MyKey& mk2)
{
int x;
return (x = mk1.string1.compare(mk2.string1)) ? x < 0 :
(x = mk1.string2.compare(mk2.string2)) ? x < 0 :
(x = mk1.string3.compare(mk2.string3)) ? x < 0 :
mk1.uint1 < mk2.uint1;
}
来源:https://stackoverflow.com/questions/10276358/std-map-composite-key