Is there any reason to use std::map::emplace() instead of try_emplace() in C++1z?

In C++17, std::map and std::unordered_map got a new member-function template: try_emplace(). This new addition, proposed in n4279, behaves similarly to emplace(), but has the following advantages:

  • try_emplace() does not move from rvalue arguments if the insertion does not happen. This is useful when manipulating maps whose values are move-only types, such as std::unique_ptr.
  • try_emplace() treats the key and the arguments to the mapped_type separately, which makes it somewhat more intuitive than generic mutators that are expressed in terms of value_type (which is std::pair).

Given the above advantages, would you ever use emplace() from C++11 instead of try_emplace() from C++1z when writing C++1z-only code?


Solution 1:

try_emplace can indeed replace most uses of emplace, but if you have an unusual use case of a map with a non-copyable and immovable key type, try_emplace will not work because it copies or moves the key. In that case, you must use emplace with std::pair's piecewise construction constructor to avoid copies and moves.

Even if your key type is copyable and/or moveable, piecewise construction is the only way to avoid copy or move constructing the key, so there might be cases when you prefer that over try_emplace.

Solution 2:

try_emplace also doesn't support heterogenous lookup - it can't, because it takes the key.

Suppose we have a std::map<std::string, int, std::less<>> counts; and a std::string_view sv. I want to do the equivalent of ++counts[std::string(sv)];, but I don't want to create a temporary std::string, which is just wasteful, especially if the string is already present in the map. try_emplace can't help you there. Instead you'd do something like

if(auto lb = counts.lower_bound(sv); lb != counts.end() && lb->first == sv) {
    ++lb->second;
}
else {
    counts.emplace_hint(lb, sv, 1);
}