The answer of L.F is using map::emplace
is the way to go if the map's value type does not require a constructor, as each time the constructor is called as shown in the example below.
C++17 comes with insert_or_assign
which is slightly different.
Probably the most efficient solution is to do something like this:
template <class M, class Vp>
std::pair<typename M::iterator, bool> insert_or_create(M& map, typename M::key_type&& k, Vp&& v) {
auto p = map.lower_bound(k);
if (p != map.end()) {
return std::make_pair(p, false);
}
return std::make_pair(map.emplace_hint(p, std::move(k), std::forward<Vp>(v)), true);
}
Here is an example that shows how it works:
#include <sstream>
#include <iostream>
#include <string>
#include <map>
class Element {
public:
Element(int value) : value(value) {
std::cout << "Element ctor value = " + std::to_string(value) << std::endl;
}
int value;
};
template <class M, class Vp>
std::pair<typename M::iterator, bool> insert_or_create(M& map, typename M::key_type&& k, Vp&& v) {
auto p = map.lower_bound(k);
if (p != map.end()) {
return std::make_pair(p, false);
}
return std::make_pair(map.emplace_hint(p, std::move(k), std::forward<Vp>(v)), true);
}
int main(int argc, char **argv) {
std::map<int, Element> map;
auto& e1 = *map.emplace(1, Element(1)).first;
std::cout << "Element in map: " << std::to_string(e1.second.value) << std::endl;
auto& e11 = *map.emplace(1, Element(11)).first;
std::cout << "Element in map: " << std::to_string(e11.second.value) << std::endl;
auto e2 = *map.insert_or_assign(2, 2).first;
std::cout << "Element in map: " << std::to_string(e2.second.value) << std::endl;
auto e22 = *map.insert_or_assign(2, 22).first;
std::cout << "Element in map: " << std::to_string(e22.second.value) << std::endl;
auto e3 = *insert_or_create(map, 3, 3).first;
std::cout << "Element in map: " << std::to_string(e3.second.value) << std::endl;
auto e33 = *insert_or_create(map, 3, 33).first;
std::cout << "Element in map: " << std::to_string(e33.second.value) << std::endl;
}
which produces
Element ctor value = 1
Element in map: 1
Element ctor value = 11 <-- calling ctor, not optimized away
Element in map: 1 <-- still old value in map as expected
Element ctor value = 2
Element in map: 2
Element ctor value = 22 <-- calling ctor
Element in map: 22 <-- new value assigned to key 2, as expected
Element ctor value = 3
Element in map: 3 <-- ctor not called as wanted!!!!!!
Element in map: 3
This shows that insert_or_create
does not call the constructor of Element
.
I am not sure why such a function is not in the std::map interface as it is pretty useful.
auto v = (m.find(k)!=m.end()?m[k]:"default")
except that is ridiculously long and complicated compared to an elvis operator. – Demonismv = (m.find(k)!=m.end()?m[k]:m[k]={p1,p2});
which returns a reference to the existing or newly constructed object from the map. I don't think there is an easier way... – Demonisminsert
oremplace
is better. – JamiejamiesongetOrDefault
in a wrong manner, if you want to insert into the map if it does not exist already thencompute
is the method to use. – Laurellaurella