lewiatan
lewiatan

Reputation: 1176

Incrementing iterator that is used as first and last of std::multimap erase range

While running an example that shows how to erase a range from std::map/multimap I have noticed strange behaviour in the following code:

#include <map>
#include <iostream>
#include <string>

int main()
{
    std::multimap<int, std::string> myMap;

    myMap.insert(std::make_pair(3, "three1"));
    myMap.insert(std::make_pair(3, "three2"));
    myMap.insert(std::make_pair(3, "three3"));
    myMap.insert(std::make_pair(45, "fourty five"));
    myMap.insert(std::make_pair(-1, "minus one"));

    std::multimap<int, std::string>::iterator iter = myMap.find(3);
    if (iter != myMap.end()) {
        myMap.erase(iter, iter++); //segmentation fault(!)
    }

    for (auto element : myMap) {
        std::cout << element.first << " -> " << element.second << std::endl;
    }
    return 0;
}

Which I build with command g++ --std=c++11 main.cpp (I use g++ 5.2.1).

Why post-incrementation of my iterator causes a Segmentation fault? I would rather say that this should create 2 copies of this iterator, pass them into the erase method, "erase nothing" just as would code myMap.erase(iter, iter); and then increment the iter.

What logic stands behind this segfault?

Is this an invalid use of iter iterator? If so - why?

BTW. It compiles when I use pre-incrementation myMap.erase(iter, ++iter) and here it "erase nothing" as I mentioned above.

Upvotes: 2

Views: 163

Answers (1)

Christophe
Christophe

Reputation: 73366

The order of evaluation of the arguments to a function call is not defined. So when you write:

   myMap.erase(iter, iter++); //segmentation fault(!)

the compiler is free to evaluate the second argument first, or not. As you use the same iterator, but have a side effect, you get Undefined Behaviour (refer to C++ standard, section 1.9/15).

For example, if the compiler evaluates first the second argument iter++, the incremented iterator would be used as the first argument, while second argument is not incremented iter. As a consequence: the range passed to erase() would be [std::next(iter), iter)- the function might attempt to erase elements that are out of range (i.e. UB).

As suggested by David in the comments, you can solve the issue with iter = myMap.erase(iter) (or using a range without side effects).

Upvotes: 4

Related Questions