Почему stl map вставляет другое значение, если ключ уже существует, а не просто меняет его?

Почему моя программа, использующая карты STL, вставляет значения, когда ключи уже существуют, вместо изменения существующих значений?

#include <iostream>
#include <map>

using namespace std;

struct CTest
{
int a, b, c;
CTest(int A, int B, int C) : a(A), b(B), c(C) {}
};

bool operator<(const CTest & l, const CTest & r)
{
if(l.a < r.a) return true;
else if(l.a > r.a) return false;

if(l.b < r.b) return true;
else if(l.b > r.b) return false;

if(l.c < r.c) return true;
else if(l.c > r.c) return false;

return true;
}

struct CTest2
{
bool operator<(const CTest2 & r) const
{
return q < r.q && w < r.w;
}
int q,w;
CTest2() : q(0), w(0) {}
CTest2(int Q, int W) : q(Q), w(W) {}
};

int main()
{
// map of maps
map<CTest, map<string, CTest2> > x;

x[CTest(1,1,1)]["lol"] = CTest2(1,2); // x[CTest(1,1,1)]["lol"] now stores CTest2(1,2)
x[CTest(1,1,1)]["lol"] = CTest2(3,4); // CTest2(1,2) must be changed to CTest2(3,4)
x[CTest(1,1,1)]["lol"] = CTest2(5,6); // now to CTest2(5,6)

x[CTest(2,1,0)]["ha"] = CTest2(11,12);

for(map<CTest, map<string, CTest2> >::iterator i = x.begin(); i != x.end(); ++i)
for(map<string, CTest2>::iterator j = i->second.begin(); j != i->second.end(); ++j)
cout << j->first << " " << j->second.q << " " << j->second.w << endl;
}

Выполнение этой печати:

lol 3 4
lol 1 2
ha 11 12

Почему это происходит и как мне это исправить?

5

Решение

Функция сравнения, которая std::map использует для сортировки элементов должен придерживаться в строгий слабый порядок. Но ваша реализация этого не делает. Согласно вашей реализации, когда все члены (a, b, c) равный, ваш operator< возвращается true, Другими словами, (1,1,1) < (1,1,1) возвращается true, Имеет ли это смысл? Нет.

Это легко исправить:

bool operator<(const CTest & l, const CTest & r)
{
if(l.a < r.a) return true;
else if(l.a > r.a) return false;

if(l.b < r.b) return true;
else if(l.b > r.b) return false;

return l.c < r.c;
}

Это слишком многословно. Вместо <, если вы используете !=вышеизложенное сведет к этому:

bool operator<(const CTest & l, const CTest & r)
{
if(l.a != r.a) return l.a < r.a;
else if(l.b != r.b) return l.b < r.b;
return l.c < r.c;
}

Ну, это все еще многословно, вы можете реализовать это как:

bool operator<(const CTest & l, const CTest & r)
{
return std::tie(l.a,l.b,l.c) < std::tie(r.a,r.b,r.c);
}

std::tie функция возвращает std::tuple чья operator< реализует строгое слабое упорядочение, поэтому воспользуйтесь этим фактом.

13

Другие решения

Ваш operator<() возвращается true для равных элементов.

8

Код, который вы указали, содержит ошибку в operator <: возвращается true когда ключи равны. Это дает утверждение отладки во время выполнения в 'x[CTest(1,1,1)]["lol"] = CTest2(3,4);, Если вы измените return true; в return false; это будет работать как задумано.

1

Ваш < оператор для CTest неверен. Вы возвращаете истину в тех случаях, когда все члены l равны членам r. Если l == r, как оно меньше r? По сути, это означает, что карта не видит каждый CTest (1,1,1) как эквивалентный, поэтому будет создавать новое отображение каждый раз, когда вы его назначаете.

1
По вопросам рекламы [email protected]