5

An iterator into a std::set becomes invalidated if the item it's pointing to is erased. (It does not get invalidated if the set is modified in any other way, which is nice.) However, there is no way to detect whether an iterator has been invalidated or not.

I'm implementing an algorithm that requires me to be able to keep track of members of a std::set in such a way that I can erase them in constant time, but without risking undefined behaviour if I try to delete the same one twice. If I have two iterators pointing to the same member of a set, Bad Things will happen if I try to erase both of them.

My question is, how can I avoid this? Is there some way to implement something that behaves like an iterator into a set, but which knows when it has been invalidated?

Incidentally, I'm using std::set because this is a performance critical situation and I need the complexity guarantees that set provides. I'm happy to accept answers that suggest a different data structure, but only if it allows me to (a) access and remove the smallest element in constant time, (b) remove the pointed-to elements in constant time, and (c) insert elements in O(log(N)) time or better. C++11 is OK.

N. Virgo
  • 7,970
  • 11
  • 44
  • 65
  • 1
    There's no way for an iterator to know that it has been invalided. – 101010 Aug 09 '14 at 05:31
  • 2
    Yeah, I said that in the first paragraph. I'm hoping to find some way around that by implementing something that's somehow iterator-like. – N. Virgo Aug 09 '14 at 05:32
  • I'm struggling to see a way out of this besides maintaining the set without erasure and simply "marking" the multi-itereator-referenced item as "invalid", with said mark *not* participating in item identity (that's *very* important). Dreadful, I know. – WhozCraig Aug 09 '14 at 05:38
  • @rici not by replacing set with map, rather something like replacing `item` with `std::pair`, and using `it->second = false` to "mark" the referenced item as invalid rather than actually erasing. Something like that would fulfill the characteristics of the question, but I doubt it would fit in algorithms the OP has already employed. – WhozCraig Aug 09 '14 at 05:44
  • @WhozCraig I'd discounted that possibility before I asked the question, but now I'm thinking it might not be that bad, because the time complexity of (1) marking the object as invalid, and then (2) discarding it when it gets popped off the head at some later time, is still constant in the number of erased elements. So most likely I'll just do that - thanks for helping me see it! – N. Virgo Aug 09 '14 at 05:48
  • @Nathaniel Benjamins idea has solid merit too. It puts the onus of that "invalid" state as pairing it with the iterator rather than maintaining it with the item in-set as I suggested. But it adds the benefit of maintaining the set integrity for content (item count, etc), which may be important to you. Slick idea. – WhozCraig Aug 09 '14 at 05:50
  • @WhozCraig: The difference between `set>` and `map` is not very substantial. But I deleted the comment because I thought you meant to replace `set` with `set>` which maintains the complexity requirements but is, in practice, much more complex. – rici Aug 09 '14 at 05:52
  • @WhozCraig yes, that's a very good point - those things might well become important to me. I like both solutions. – N. Virgo Aug 09 '14 at 06:51
  • @WhozCraig: Note that with `set>`, The 0(1) requirement to find the smallest item is lost as finding the first non-marked element require iteration. – Jarod42 Aug 09 '14 at 07:23

1 Answers1

7

You could keep a set of shared pointers. And every time you store an iterator, pair it with a weak pointer to the element. When you want to erase the element, first check the weak pointer to see if the object still exists.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • 1
    +1 I like this approach because it maintains the integrity of the set sizing. If that is important to the OP, it should *definitely* be considered. – WhozCraig Aug 09 '14 at 05:51
  • 1
    It is to be noted that a custom comparator for the pointed-to objects must be used as the default one would just compare the shared pointers. – Mark Garcia Aug 09 '14 at 05:54