Very often I encounter such pattern: I want to read value from the map under some key. If the key does not exist, I want to compute a default value (which can be costly, so I do it only when I must), and insert it into map under that key. But: I want it to be efficient - I want to do lookup in the map exactly once, regardless the key existed or not. Is it possible? This is a typical pattern for implementing caches.
Well one thing worth remembering is that std::map::orerator[] will create an element under the given key if one does not already exist. So you can arrange for the map contents to be constructed with a suitable default:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#include <map>
#include <iostream>
struct data_t
{
int value;
data_t(): value(100){} // default value 100
};
int main()
{
std::map<std::string, data_t> datamap;
// If key "item" does not exist then a new one is created with value 100
int value = datamap["item"].value;
std::cout << "value: " << value << std::endl;
}
template <typename keyT,typename valT>
typename std::map<keyT,valT>::iterator FindOrInsert(std::map<keyT,valT>& mymap,const std::pair<keyT,valT>& insertion)
{
// if the map is empty, just insert the new value
if(mymap.empty())
return mymap.insert(insertion).first;
// otherwise find the lower_bound
typename std::map<keyT,valT>::iterator i = mymap.lower_bound( insertion.first );
// if i == end, or if i is not a match, then this element was not in the map, so we need to insert it
if(i == mymap.end() || i->first != insertion.first)
{
// move i to the element preceeding the insertion point
if(i != mymap.begin())
--i;
// insert it
i = mymap.insert(i,insertion);
}
return i;
}
Usage would be as follows:
1 2 3 4 5 6 7 8
std::map<int,int> mymap;
// fill mymap here
// need to see if mymap has a key of [50]. If not, create the key of [50] with value 25
std::map<int,int>::iterator i = FindOrInsert( mymap, std::pair<int,int>(50,25) );
std::cout << i->first; // will print '50', as i is pointing to the proper element
Ok, maybe I forgot to mention that the default value should be based on the key. Otherwise, this would be useless for caching. So, Galik's solution is not what I wanted. Disch solution also suffers from the same problem - the default is computed before checking for presence of the element.
The idea of cache is **not** to compute a value if it can be retrieved from the map.