С++ найти и удалить элемент мультикарты

Мне нужно добавить, сохранить и удалить некоторые пары объектов, например. Person-Hobby. У каждого человека может быть несколько увлечений, а у нескольких людей может быть одно и то же хобби. Итак, multimap — хороший контейнер, верно?

Перед добавлением пары мне нужно знать, если она еще не добавлена. Как я вижу, здесь нет стандартного метода класса, чтобы знать, если конкретный пара, например Peter-Football существует в ММ. Таким образом, я написал метод, который возвращает положительное целое число (равное расстоянию между mm.begin() и итератором пары), если пара существует, и -1 в противном случае.

Затем мне нужно удалить пару. Я вызываю свой метод find, который возвращает некоторое положительное целое число. Звоню myMultiMap.erase(pairIndex); но пара почему-то не удаляется. Это моя проблема. Очевидно, что метод erase нуждается в iterator, а не в int. Вопрос: как мне преобразовать целое число в итератор?

Спасибо!

ОБНОВЛЕНИЕ: я пробовал это c.begin() + int_value, но получил ошибку error: no match for ‘operator+’ в этой строке....


person Kolyunya    schedule 08.10.2012    source источник
comment
Ваш пользовательский метод find предположительно использует итераторы, чтобы выяснить, существует ли элемент; просто используйте этот итератор, если он найден.   -  person tmpearce    schedule 08.10.2012
comment
@tmpearce, если бы мои функции возвращали итератор, как бы я узнал, если пара не завершается?   -  person Kolyunya    schedule 08.10.2012
comment
Разве std::set<std::pair<Person, Hobby>> не будет более подходящим контейнером?   -  person Kerrek SB    schedule 08.10.2012
comment
Как я вижу, здесь нет способа узнать, если конкретная пара, например. Питер-Футбол существует в ММ. неправильно: cplusplus.com/reference/stl/multimap/equal_range + итерация от результата.первый к результату.секунда и проверка, если он-›второй==Футбол   -  person NoSenseEtAl    schedule 08.10.2012
comment
@NoSenseEtAl, очевидно, я имел в виду стандартный метод класса, а не самописный метод.   -  person Kolyunya    schedule 08.10.2012


Ответы (3)


Не то чтобы я одобрял ваш подход, но если int - это расстояние между begin() и рассматриваемым итератором, вы можете просто использовать

c.begin() + int_value

or

std::advance(c.begin(), int_value)

чтобы получить итератор. Вторая версия необходима для итераторов, которые не являются итераторами произвольного доступа.

В интересах вашего личного здравомыслия (и скорости программы) я предлагаю вам вернуть итератор напрямую в той или иной форме.

Есть много возможных интерфейсов, которые так или иначе решают эту проблему. То, что я бы назвал «старым способом C», будет возвращаться по выходному параметру:

bool find_stuff(stuff, container::iterator* out_iter) {
 ...
 if(found && out_iter)
   *out_iter = found_iter;
 return found;
}

используй это:

container::iterator the_iter;
if(find_stuff(the_stuff, &the_iter)) ...

or

if(find_stuff(the_stuff, 0)) // if you don't need the iterator

Это не идиоматический C++, но Линусу это понравилось бы.

Вторая возможная и теоретически обоснованная версия использует что-то вроде boost::optional для возврата значения. Таким образом, вы возвращаете либо некоторое значение, либо ничего.

boost::optional<container::iterator> find_stuff(stuff) {
 ...
 if(found && out_iter)
   return found_iter;
 return boost::none;
}

Использовать:

boost::optional<container::iterator> found = find_stuff(the_stuff);
if(found) {
  do something with *found, which is the iterator.
}

or

if(find_stuff(the_stuff)) ...

Третье возможное решение будет идти std::set::insert путем, т.е. возвращая пару, состоящую из флага и значения:

std::pair<bool, container::iterator> find_stuff(stuff) {
 ...
 return std::make_pair(found, found_iter);
}

Использовать:

std::pair<bool, container::iterator> found = find_stuff(the_stuff);
if(found.first) ...
person jpalecek    schedule 08.10.2012
comment
Я получаю сообщение об ошибке error: no match for ‘operator+’ в строке c.begin() + int_value.... Что я делаю не так? - person Kolyunya; 08.10.2012
comment
@Kolynya: ваш итератор не является произвольным доступом. - person jpalecek; 08.10.2012

Подумайте о том, чтобы изменить свой mulitmap<Person,Hoobby> на set<pair<Person,Hobby> > - тогда у вас не будет проблем, которые есть сейчас. Или подумайте о смене на map<Person, set<Hobby> >. Оба варианта не позволяют вставлять повторяющиеся пары.

person PiotrNycz    schedule 08.10.2012
comment
Мне нужно будет найти все увлечения конкретного человека. Карта позволит сделать это быстрее, так как хранит отсортированные значения, верно? - person Kolyunya; 08.10.2012
comment
Я обновил свой ответ. Второй вариант — map<Person,set<Hobby>> — так у вас будет быстрый доступ к увлечениям человека. - person PiotrNycz; 08.10.2012

Используйте 2 набора (не несколько наборов) один для хобби и один для людей, эти два действуют как фильтры, поэтому вы не добавляете одного и того же человека дважды (или хобби). операции вставки в этих наборах дают итератор для вставляемого элемента (или «правильный» итератор для элемента, если он уже был вставлен). Два итератора, которые вы получаете при вставке в hobbies_set и person_set, теперь используются как ключ и значение в мультикарте.

Использование третьего набора (не multi_set) для отношения вместо multi_map может дать преимущество в том, что не нужно проверять перед вставкой отношения, если оно уже есть, оно не будет добавлено снова, а если его нет, оно будет добавлено. . В обоих случаях он вернет итератор и логическое значение (сообщает, было ли оно уже там или было добавлено)

структуры данных:

typedef std::set<Hobbie> Hobbies;
typedef std::set<Person> Persons;
typedef std::pair<Hobbies::iterator,bool> HobbiesInsertRes;
typedef std::pair<Persons::iterator,bool> PersonsInsertRes;
struct Relation {
  Hobbies::iterator hobbieIter;
  Persons::iterator personIter;
  // needed operator<(left for the as an exercies for the reader);
};
typedef set<Relation> Relations;

Hobbies hobbies;
Persons persons;
Relations relations;

вставлять:

HobbiesInsertRes hres = hobbies.insert(Hobbie("foo"));
PersonsInsertRes pres = persons.insert(Person("bar"));
relations.insert(Relation(hres.first, pres.first));
// adds the relation if does not exists, if it allready did exist, well you only paid the same amount of time that you would have if you would to do a check first.

Погляди:

// for a concrete Person-Hobbie lookup use 
relations.find(Relation(Hobbie("foo"),Person("Bar")));

// to find all Hobbies of Person X you will need to do some work.
// the easy way, iterate all elements of relations
std::vector<Hobbie> hobbiesOfX;
Persons::iterator personX = persons.find(Person("bar"));
std::for_each(relations.begin(), relations.end(), [&hobbiesOfBar, personX](Relation r){
  if(r.personIter = personX)
    hobbiesOfX.push_back(r.hobbieIter);
});

// other way to lookup all hobbies of person X
Persons::iterator personX = persons.find(Person("bar"));  
relations.lower_bound(Relation(personX,Hobbies.begin()); 
relations.upper_bound(Relation(personX,Hobbies.end());
// this needs operator< on Relation to be implemented in a way that does ordering on Person first, Hobbie second.
person Jim Hansson    schedule 08.10.2012
comment
Затем мне нужно будет найти все увлечения конкретного человека. M-Map позволит сделать это быстрее, так как хранит отсортированные значения, верно? - person Kolyunya; 08.10.2012
comment
обе карты и наборы сортируются только по ключам, а не по значениям - person Jim Hansson; 08.10.2012