简体   繁体   中英

Behaviour of map emplace vs try_emplace?

Let's say I have an unordered_map:

std::unordered_map<Key, BigObject> big_objects;

where BigObject is like:

struct BigObject {
    BigObject(P1, P2, P3); // <--- expensive
    /*...*/
};

and I have a Key k , and a set of arguments for BigObject s constructor P1 p1 , P2 p2 and P3 p3 .

If big_objects already contains k I want to do nothing. If big_objects does not contain K I want to construct and insert BigObject(p1,p2,p3)

Is it possible to do this with a single lookup on big_objects ?

ie Will emplace or try_emplace construct BigObject if the key is already present?

Update

After some testing it seems like emplace does construct BigObject but try_emplace doesn't?

ie

big_objects[k] = BigObject(p1,p2,p3) // insert k, k now present...

big_objects.emplace(k, p1, p2, p3); // does construct a BigObject

big_objects.try_emplace(k, p1, p2, p3); // does not construct a BigObject

Is this correct?

Yes, it is correct, this is the reason why try_emplace exists, it doesn't touch its arguments if the key is already present.

Cppreference try_emplace

Unlike insert or emplace, these functions do not move from rvalue arguments if the insertion does not happen, which makes it easy to manipulate maps whose values are move-only types, such as std::map<std::string, std::unique_ptr>. In addition, try_emplace treats the key and the arguments to the mapped_type separately, unlike emplace, which requires the arguments to construct a value_type (that is, a std::pair)

As a bonus point, you do not have to use the ugly std::piecewise_construct tag to pass both the key and the value constructor arguments ( unless you want to inplace construct the key too).

Yes, like this algorithm problem: enter image description here

One solution is:

int findShortestSubArray(vector<int>& nums) 
{
    std::unordered_map<int, std::tuple<int, int, int>> map;
    int s = nums.size();
    for(int i = 0; i < s; ++i)
    {
        if(!map.try_emplace(nums.at(i), std::tuple<int, int, int>{1,i,i}).second)
        {
            ++std::get<0>(map.at(nums.at(i)));
            std::get<2>(map.at(nums.at(i))) = i;
        }                
    }
    //...
}

This solution takes the result of its execution from the return value of try_emplace() and uses it as a condition for an if statement, end up with a very nice space-time overhead:

Execution Time: 32ms, beating 97.50% of users in all C++ commits
Memory consumption: 24.3MB, beating 93.47% of users in all C++ commits

but if I replace try_emplace() with emplace() , Space-time costs will balloon:

Execution Time: 84ms, beating 61.24% of users in all C++ commits
Memory consumption: 33.8MB, beating 6.65% of users in all C++ commits

This reason has been answered by other friends, and I think that's a good example of the difference between the two.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM