Мне нужно добавить, сохранить и удалить несколько пар объектов, например, Person
—Hobby
, Любой человек может иметь несколько хобби, и несколько человек могут иметь одно и то же хобби. Итак, multimap — это хороший контейнер, верно?
Прежде чем добавить пару, мне нужно знать, если она еще не добавлена. Как я вижу Вот нет никакого стандартного метода класса, чтобы знать, конкретна ли пара, например. Peter-Football
существует в ММ. Таким образом, я написал метод, который возвращает положительное целое число (равное расстоянию между mm.begin () и итератором пары), если пара существует и -1
иначе.
Затем мне нужно удалить пару. Я звоню своему find
метод, который возвращает некоторое положительное целое число. Я звоню myMultiMap.erase(pairIndex);
но пара не удаляется по какой-то причине. Это моя проблема. Очевидно, что erase
метод нуждается в iterator
, не int
, Вопрос в том, как преобразовать целое число в итератор?
Спасибо!
ОБНОВИТЬ:
Я пробовал это c.begin() + int_value
но получил ошибку error: no match for ‘operator+’
на этой линии ….
Не то чтобы я одобряю твой подход, но если int
это расстояние между begin()
и рассматриваемый итератор, вы можете просто использовать
c.begin() + int_value
или же
std::advance(c.begin(), int_value)
чтобы получить итератор. Вторая версия необходима для итераторов, которые не являются итераторами с произвольным доступом.
В интересах вашего личного здравомыслия (и скорости работы программы) я бы посоветовал вам вернуть итератор напрямую в какой-либо форме.
Есть много возможных интерфейсов, которые решают это так или иначе. То, что я бы назвал «старым способом C», будет возвращаться параметром out:
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)) ...
или же
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.
}
или же
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) ...
Подумайте, чтобы изменить свой mulitmap<Person,Hoobby>
в set<pair<Person,Hobby> >
— тогда у вас не будет проблем у вас сейчас. Или подумайте, чтобы изменить map<Person, set<Hobby> >
, Оба варианта не позволят вставить дубликаты пар.
Используйте 2 набора (не несколько наборов), один для хобби и один для людей, эти два действуют как фильтры, чтобы вы не добавляли одного и того же человека дважды (или хобби). Операции вставки в этих наборах дают итератор для вставляемого элемента (или «правильный» итератор для элемента, если он уже был вставлен). Два итератора, которые вы получаете от вставки в hobbies_set и person_set, теперь используются в качестве ключа и значения в мультикарте.
Использование третьего набора (не multi_set) для отношения вместо multi_map может дать преимущество в том, что нет необходимости проверять перед вставкой отношения, если оно уже есть, оно не будет добавлено снова, а если его нет, оно будет добавлено , В обоих случаях он возвращает итератор и bool (сообщает, был ли он уже там или был добавлен)
datastructures:
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.