3

While looking for an efficient approach to insert to a map only if the key does not exist, I came across this approach:

MapType::iterator lb = mymap.lower_bound(k);

if(lb != mymap.end() && !(mymap.key_comp()(k, lb->first))) {
    // key exists. Value accessible from lb->second
} else {
    // Do insert. Use lb as a hint to insert so it can avoid another lookup
    mymap.insert(lb, MapType::value_type(k, v));
}

That works well for std::map. However, boost::ptr_map does not provide a similar form of insert() i.e. one that accepts an iterator position.

So I'm wondering:

  1. What's the benefit of that approach compared to doing a straight-forward insert? i.e.

    std::pair<MapType::iterator, bool> ret;
    ret = mymap.insert(MapType::value_type(k, v));
    if (!ret.second) {
        // key exists. insertion not done. do something else
    }
    
  2. If there's indeed a good reason to use the lower_bound approach, is it there an equivalent strategy for boost::ptr_map? Or would it not be applicable?

Community
  • 1
  • 1
Shawn Chin
  • 84,080
  • 19
  • 162
  • 191

1 Answers1

1

there are two most efficient ways to do this.

The first efficient way is just to call insert (as is also the case in STL). This returns pair<iterator,bool> so if it already exists it doesn't insert.

The second way, which you use when you have to create an object if the key doesn't exist, is to use operator[] which returns you a reference to what is there. If the item is not there, it inserts for you with a default-created value.

Note the difference here: for a regular STL map to pointers, operator[] will return you a pointer, and will insert a null one. for boost::ptr_map it will not insert a null pointer, it will insert a pointer to a default-constructed object.

If your collection may actually contain default-constructed objects, and you don't yet have the object you want to insert, I cannot find an efficient way to do it in one go. Perhaps don't use this collection in this case, or ensure that your objects are modified slightly so you have some kind of flag to show it is "default constructed" i.e. a sort of null state.

CashCow
  • 30,981
  • 5
  • 61
  • 92
  • Interesting, it is showing me as having originally answered this question 4 minutes before it was asked. – CashCow Nov 12 '12 at 16:10
  • Many thanks. Your first suggestion is indeed what I was using until I came across the approach I quoted in the example. In my attempts to compare both approaches, it came unstuck with `ptr_map`. My original question wasn't very well written and a little misleading. Now updated. Sorry. – Shawn Chin Nov 12 '12 at 17:09
  • 2
    The words "most efficient" are arguable — it depends on a use case. It is possible that creating an object to insert is very costly, thus checking first is needed, that's where `lower_bound` could come into play. –  Nov 12 '12 at 17:17
  • @VladLazarenko yes, that is why with a normal map you would first insert with operator[]. If your type is either a raw pointer or shared_ptr, will get a reference back and will be able to ascertain that it has inserted because you got a null. Your other option is to call insert() with a null and look at the return value . IF the bool is true you inserted so overwrite the iterator (which is not const) with the new value. You can't do either of these with ptr_map though which requires a proper non-const pointer and returns a newly created if it inserts with operator[]. – CashCow Nov 13 '12 at 09:35