3
  1. How often do std::multimap and std::unordered_multimap shuffle entries around? I'm asking because my code passes around references to distinguish between entries with the same hash, and I want to know when to run the reference redirection function on them.

  2. What happens if I do this:

    std::multimap atable; //Type specification stuff left out
    //Code that pus in two entries with the same key, call that key foo
    int bar = atable[foo];
    
  3. Is the result different if it's an unordered_multimap instead?

  4. Back to passing references around to distinguish entries with the same hash. Is there a safer way to do that?

  5. Do the entries move around if I remove one of the entries (That's what's suggested by a reading of the documentation for std::vector)?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
eaglgenes101
  • 169
  • 5
  • For [`std::unordered_map`](http://en.cppreference.com/w/cpp/container/unordered_map) there is really no need to shuffle anything around, as its not sorted. – Some programmer dude Aug 17 '13 at 19:16
  • And considering that [`std::map::operator[]`](http://en.cppreference.com/w/cpp/container/map/operator_at) returns references, I find it unlikely that they will be moved either. – Some programmer dude Aug 17 '13 at 19:19

3 Answers3

5

NO, no elements will be harmed during any operation.

As is explained in this famous Q&A, for associative containers, there is no iterator invalidation upon insertions / erasure (except for the element being erased of course). For unordered associative containers, there is iterator invalidation during rehashing, about which the Standard says (emphasize mine)

23.2.5 Unordered associative containers [unord.req]

9 The elements of an unordered associative container are organized into buckets. Keys with the same hash code appear in the same bucket. The number of buckets is automatically increased as elements are added to an unordered associative container, so that the average number of elements per bucket is kept below a bound. Rehashing invalidates iterators, changes ordering between elements, and changes which buckets elements appear in, but does not invalidate pointers or references to elements. For unordered_multiset and unordered_multimap, rehashing preserves the relative ordering of equivalent elements.

Again, this does not entail the reshuflling of the actually stored elements (the Key and Value types in unordered_map<Key, Value>), because unordered maps have buckets that are organized as linked lists, and iterators to stored elements (key-value pairs) have both an element pointer and a bucket pointer. The rehashing shuffles buckets, which invalidates the iterators (because their bucket pointer is invalidated) but not pointers or references to the elements itself. This is explained in detail in another Q&A

Community
  • 1
  • 1
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
2

How often do std::multimap and std::unordered_multimap shuffle entries around?

Never. The iterators that point to elements of any associative container (including sets, maps, and their unordered or "multi" versions) are never invalidated (unless the specific element they point to is deleted). In other words, the actual elements are never "shuffled around". These are required to be implemented as linked structures (e.g., linked-tree), meaning they can be re-structured just by changing a few pointers, without having to physically move any element.

EDIT: Apparently (see TemplateRex' comment), this is not the case for unordered containers. In that case, the iterators can get invalidated, but the elements themselves do not move around. These requirements imply an indirect container with no back-pointer, which I guess is a reasonable choice, but not one I would have expected.

What happens if I do this: ... (get [] of multimap) ...

The operator[] is not defined for std::multimap (or unordered version). So, what would happen? A compiler error would happen.

Is the result different if it's an unordered_multimap instead?

No, it's the same, the operator[] does not exist.

Back to passing references around to distinguish entries with the same hash. Is there a safer way to do that?

Yes, the recommended practice is to refer to elements of the map / set / whatever using iterators, not references. The iterators to elements are guaranteed to remain valid, and they are copyable and have the right const-ness protection on them, and that makes them the perfect objects to "refer to an entry".

EDIT: As per the same comment, I would have to recommend using pointers to the elements if dealing with a hashed container (unordered containers), because iterators are not guaranteed (by the standard) to remain valid.

Mikael Persson
  • 18,174
  • 6
  • 36
  • 52
  • 1
    -1 rehashing invalidates iterators for `unordered_multimap`, but not pointers or references – TemplateRex Aug 17 '13 at 20:36
  • @TemplateRex Thanks for pointing that out. I had a slight suspicion that the standard might allow an implementation to invalidate iterators of unordered containers, but not a strong enough suspicion to look it up. I guess I was wrong. I deserved that down-vote. – Mikael Persson Aug 17 '13 at 21:30
1

All of the associative containers in the C++ standard library are node based, i.e., their elements stay put. However, whether the hash is computed on the object after copying it or on a temporary object passed to the container isn't specified. I would guess, that generally the hash is computed before the object is copied/moved.

To distinguish elements with the same hash you need to have an equality function anyway: if the location of the object causes it to be different it would mean that all objects are different and you wouldn't be able to look them up at all. You need to have an equality function for the elements in an unordered container which defines equivalence of keys. For the ordered associative the equivalent class is based on the strict weak ordering, i.e., on an expression like this (using < rather than a binary predicate for readability; any binary predicate defining a strict weak order would work, too):

bool equivalent = !(a < b) && !(b < a);
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380