How to use try_emplace in a nested map to still meet the purpose what emplace achieve?

196 Views Asked by At

First I have codes like

typedef struct st_A{
    int a = 0;
    string s;

    st_A(const int a, const string&s) :a(a),s(s) {}
}st_A;

map<string, st_A> m1;

Now I want to insert a new element of pair{"c1", st_A{10, "c"}} into this map, so I code like

if (auto it = m1.find("c1"); it == m1.end()){
    //not find
    m1.insert({ "c1", {10, "c"} });
}else{
    ....update by using it....
}

But replacing by try_emplace I think it would be more concise and efficient

auto [it, inserted] = m1.try_emplace("c1", 10, "c");
if (!inserted) {
    ....update by using it....
}

So is it really more efficient in the form try_emplace than the one of find+insert?

And if my original map changes into map<string, map<int, st_A>> m2, now I want to insert insert a new element of pair{"c1", pair{1, st_A{10, "c"}}}; if I still insist on using try_emplace,but it would be ill-formed like

auto [it, inserted] = m1.try_emplace("c1", 1, 10, "c");

So would it still be suitable this time if using try_emplace, and how?

1

There are 1 best solutions below

2
Jarod42 On

So is it really more efficient in the form try_emplace than the one of find+insert?

  • try_emplace does an unique look-up and potentially contruct in-place.
  • find+insert do 2 look-up and use copy/move constructor.

Another alternative would be lower_bound+insert(with hint) to reduce the look-up to one, but it would need extra check to see if found iterator is the value or not.

So try_emplace win in all circumstance.

And if my original map changes into map<string, map<int, st_A>> m2, now I want to insert insert a new element of pair{"c1", pair{1, st_A{10, "c"}}}; if I still insist on using try_emplace, but it would be ill-formed like:

auto [it, inserted] = m1.try_emplace("c1", 1, 10, "c");
So would it still be suitable this time if using try_emplace, and how?

map cannot be construct from pair (but from initializer_list<pair<..>>).

{..} has not type, so is problematic in try_emplace as-is.

so it would be:

auto [it, inserted] = m2.try_emplace("c1", std::initializer_list<std::pair<const int, st_A>>{{1, st_A{10, "c"}}});

With the caveat that

  • you construct the list of pairs anyway
  • initializer_list content is const, so require a copy and not a move.

You might create a proxy type o construct the map only on demand, something like:

template <typename T>
struct MakeMap
{
    int key;
    int a;
    T s;

    operator std::map<int, st_A>() && { return {{key, {a, s}}}; }
};

and

auto [it2, inserted2] = m2.try_emplace("c1", MakeMap{1, 10, "c"});

Demo