The method std::vector::erase has two overloads:
iterator erase( const_iterator pos );
iterator erase( const_iterator first, const_iterator last );
The first one only remove the element at pos while the second one remove the range [first, last).
Since you forget the last iterator in your call, the first version is chosen by overload resolution, and you only remove the first pair shifted to the end by std::remove_if. You need to do this:
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool {
return stopPoint.first == 4;
}),
stopPoints.end());
The erase-remove idiom works as follow. Let say you have a vector {2, 4, 3, 6, 4} and you want to remove the 4:
std::vector<int> vec{2, 4, 3, 6, 4};
auto it = std::remove(vec.begin(), vec.end(), 4);
Will transform the vector into {2, 3, 6, A, B} by putting the "removed" values at the end (the values A and B at the end are unspecified (as if the value were moved), which is why you got 6 in your example) and return an iterator to A (the first of the "removed" value).
If you do:
vec.erase(it)
...the first overload of std::vector::erase is chosen and you only remove the value at it, which is the A and get {2, 3, 6, B}.
By adding the second argument:
vec.erase(it, vec.end())
...the second overload is chosen, and you erase value between it and vec.end(), so both A and B are erased.