tags:

views:

301

answers:

4

I have a map of objects and I want to update the object mapped to a key, or create a new object and insert into the map. The update is done by a different function that takes a pointer to the object (void update(MyClass *obj))

What is the best way to "insert or update" an element in a map?

+6  A: 

The [] operator

Terry Mahaffey
This can yield elegant code, if you want to insert a default `MyClass` object, and then update it. If you need to insert a non-default object, or not update the inserted value, Charles's solution is probably better.
Dennis Zickefoose
`update(my_map[key])`? That would also mean that you `update` always (which is not clear from the question).
UncleBens
What if the key doesn't exist?
Konrad
@Konrad: A default constructed element will be inserted and returned. That might or might not be the desired semantics, it depends on how everything works.
Dennis Zickefoose
A: 

something like:

map<int,MyClass*> mymap;
map<int,MyClass*>::iterator it;

MyClass* dummy = new MyClass();
mymap.insert(pair<int,MyClass*>(2,dummy));

it = mymap.find(2);
update(it.second);

here a nice reference link

PoweRoy
If you're going to go that route: `MyClass update(`
Dennis Zickefoose
Yes but with [] you should know that if the object doesn't exist, it creates a new one (resulting that second is a null pointer)
PoweRoy
No. If the key is not present, it inserts one, and then returns a reference to it. So you're guaranteed to get a valid object so long as there's memory for it. Even if that weren't the behavior, it certainly wouldn't yield an invalid reference. An exception is much more likely in that case.
Dennis Zickefoose
Oh, a memory leak.
Matthieu M.
@Dennis Yes it will create a new pair, but the value (it.second) is a pointer to a MyClass object that hasn't been set.
PoweRoy
OOh, you're using dynamic objects. Yes, that does make a difference. You shouldn't do that, though. Its fraught with problems.
Dennis Zickefoose
+2  A: 

With something like the following snippet:

std::map<Key, Value>::iterator i = amap.find(key);

if (i == amap.end())
    amap.insert(std::make_pair(key, CreateFunction()));
else
    UpdateFunction(&(i->second));

If you want to measure something that might improve performance you might want to use .lower_bound() to find where an entry and use that as a hint to insert in the case where you need to insert a new object.

std::map<Key, Value>::iterator i = amap.lower_bound(key);

if (i == amap.end() || i->first != key)
    amap.insert(i, std::make_pair(key, CreateFunction()));
                                       // Might need to check and decrement i.
                                       // Only guaranteed to be amortized constant
                                       // time if insertion is immediately after
                                       // the hint position.
else
    UpdateFunction(&(i->second));
Charles Bailey
I think you effectively need to decrement `i` if it's not equal to `amap.begin()`. And thus the obvious question: how to give as an hint that it should be inserted in first position ? I suppose it works with the no-hint version :/
Matthieu M.
Yes, it's a bit of a warty requirement. An implementation *could* make it efficient if the hint was immediately after the insertion position. This would seem to be neater... and this is how it's been fixed: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#233
Charles Bailey
A: 

The operator[] already does, what you want. See the reference for details.

ablaeul