0

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?

f1msch
  • 509
  • 2
  • 12
  • `typedef struct st_A` This is a C thing, and it is completely unnecessary in C++. – 康桓瑋 Feb 07 '23 at 09:02
  • 1
    `find` followed by `insert` (without hint) does 2 lookup. It also constructs mapped object by copy and not "in place". – Jarod42 Feb 07 '23 at 13:15
  • You are `find`ing one key, but inserting another. There's no single call that can do both. `insert` et al check for the presence of the same key they are inserting, of course. – Igor Tandetnik Feb 07 '23 at 15:49
  • I can't begin to guess what `m1.try_emplace("c1", 1, 10, "c");` is even supposed to do. Each entry in `m1` ultimately contains two strings and one int, but you attempt to pass two ints. Where is the second one supposed to go? Can you describe in English what you are trying to achieve? The code you show is mighty confusing. – Igor Tandetnik Feb 07 '23 at 15:52
  • @IgorTandetnik There are some typos, and I add some description about my goals and what my codes aim to use for – f1msch Feb 08 '23 at 07:17
  • @Jarod42 But is there a more efficient way by using try_emplace than insert if my map is more complex like `map>` – f1msch Feb 08 '23 at 07:31
  • Are you perhaps looking for [insert_or_assign](https://en.cppreference.com/w/cpp/container/map/insert_or_assign)? – j6t Feb 08 '23 at 07:45

1 Answers1

0

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

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • But in your demo, there are still two times of copy ctor of st_A, so does it mean it's the necessary cost to `emplace` in the case of `map>`? – f1msch Feb 09 '23 at 11:18
  • The construction of the map could be with less copy in `MakeMap`: `std::map m; m.emplace(key, a, s); return m;`. – Jarod42 Feb 09 '23 at 14:38