Mihai Todor
Mihai Todor

Reputation: 8239

std::multimap and equal_range

I'm having some headaches with the std::multimap container and I wish to know what would be the proper way to achieve my goal. Basically, here is my SSCCE:

#include <iostream>
#include <map>

int main ()
{
    typedef std::multimap<int, int> CollectionType;
    typedef std::pair<CollectionType::iterator, CollectionType::iterator> RangeType;

    CollectionType multiMap;

    multiMap.insert(std::make_pair(1, 1));
    multiMap.insert(std::make_pair(1, 2));
    multiMap.insert(std::make_pair(2, 3));
    multiMap.insert(std::make_pair(2, 4));
    multiMap.insert(std::make_pair(2, 5));
    multiMap.insert(std::make_pair(3, 1));

    RangeType range = multiMap.equal_range(2);
    for (CollectionType::iterator iterator = range.first; iterator != range.second; ++iterator)
    {
        if (iterator->second == 4)
        {
            //multiMap.erase(iterator);//ISSUE 1
        }
        else
        {
            //iterator->first = -1;//ISSUE 2
        }
    }

    return 0;
}

As you can see above, I need to select a range of the multimap for a given key and then:

  1. I need to erase certain rows from the range
  2. I need to change the key of other rows from the range

Regarding 1, since "references and iterators to the erased elements are invalidated", how can I remove those elements? Should I push the specific iterators in some container and iterate over it once the loop is over? I saw this answer, but it seems a bit hackish / ugly / error prone / etc...

Regarding 2, since the "dumb" approach won't (obviously) work, what would be a good approach to achieve what I need? I could remove the elements and insert new ones in their place, once I solve issue 1, but wouldn't that perhaps mess up the iteration if, let's say I mistakenly insert an item with the same key as the one I just removed?...

int second = iterator->second;
localEdges.smarter_erase(iterator);
localEdges.insert(std::make_pair(-1, second));

Upvotes: 3

Views: 7078

Answers (1)

congusbongus
congusbongus

Reputation: 14622

erase returns an iterator, so reset your iterator to that and it will continue to be valid.

Just reinsert with the new key, erasing the original

http://ideone.com/0Pr6Qc:

#include <iostream>
#include <map>

void printMultimap(const std::multimap<int, int>& multiMap)
{
    std::cout << "MultiMap:\n";
    for (const auto& pair : multiMap)
    {
        std::cout << pair.first << ":" << pair.second << "\n";
    }
}

int main()
{
    std::multimap<int, int> multiMap;
    multiMap.insert(std::make_pair(1, 1));
    multiMap.insert(std::make_pair(1, 2));
    multiMap.insert(std::make_pair(2, 3));
    multiMap.insert(std::make_pair(2, 4));
    multiMap.insert(std::make_pair(2, 5));
    multiMap.insert(std::make_pair(3, 1));

    printMultimap(multiMap);

    auto range = multiMap.equal_range(2);
    for (auto iterator = range.first; iterator != range.second;)
    {
        if (iterator->second != 4)
        {
            multiMap.insert(std::make_pair(-1, iterator->second));
        }
        iterator = multiMap.erase(iterator);
    }

    printMultimap(multiMap);

    return 0;
}

Upvotes: 4

Related Questions