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 asstd::unique_ptr
. -
try_emplace()
treats the key and the arguments to themapped_type
separately, which makes it somewhat more intuitive than generic mutators that are expressed in terms ofvalue_type
(which isstd::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);
}