Reputation: 140
I'm trying to solve a bug in my code and it seems that either I can't really understand the requirements for std::map::operator[] in the standard or there is something broken at least in the STL that comes with my compiler (VS2013, still in the process to try different toolsets).
The std::map I'm probably misusing is defined providing a Compare type as its third template parameter that makes some of the key objects I use equivalent with some other. As for my understanding respecting the definition of equivalence given in the standard (23.2.4.2): a and b are equivalent if: !(a<b)
AND !(b<a)
.
The problem I'm facing is that, depending on the insertion order, sometime it inserts also keys with an equivalent already there.
Here is the minimal example that I ended up:
#include <map>
#include <cassert>
struct Foo {
int x;
char c;
};
struct FooLess {
bool equal_c( const Foo& f1, const Foo& f2) const {
return f1.c == f2.c;
}
bool operator() (const Foo& f1, const Foo& f2) const {
return !equal_c(f1, f2) && f1.x < f2.x;
}
};
using namespace std;
int main(int , char* [])
{
FooLess lt;
assert( !lt( Foo {1, 'a'}, Foo{3, 'a'}) );
assert( !lt( Foo {3, 'a'}, Foo{1, 'a'}) );
map<Foo, string, FooLess> m;
m[Foo{ 2, 'b'}] = "Foo(b)";
m[Foo{ 3, 'a'}] = "Foo(A)";
m[Foo{ 4, 'c'}] = "Foo(c)";
// does not hold!
assert ((m[Foo{1, 'a'}] == "Foo(A)") );
m[Foo{ 1, 'a'}] = "Foo(a)";
// does not hold!
assert(m.size() == 3);
return 0;
}
As for my reading of the standard the assertions should hold.
23.4.4.3 map element access [map.access] T& operator[](const key_type& x);
1 Effects: If there is no key equivalent to x in the map, inserts value_type(x, T()) into the map.
2 Requires: key_type shall be CopyConstructible and mapped_type shall be DefaultConstructible.
3 Returns: A reference to the mapped_type corresponding to x in *this.
4 Complexity: logarithmic.
Why Am I wrong? Am I?
Upvotes: 1
Views: 112
Reputation: 45664
The problem is that FooLess
does not describe a strict weak ordering, but a partial ordering.
See C++ named requirements: Compare for the details.
As an example:
Foo a {1, 'a'};
Foo b {2, 'b'};
Foo c {1, 'c'};
FooLess comp;
assert(comp(a, b) && comp(b, c)); // Should imply comp(a, c)
assert(!comp(a, c)); // But doesn't due to only partial ordering
Your comparator works as long as all of the keys in the map or searched for have unique .c
s.
Upvotes: 4