64

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++17 when writing C++17-only code?

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
s3rvac
  • 9,301
  • 9
  • 46
  • 74
  • 1
    `emplace` might be a few cycles faster because it doesn't check whether insertions happen, but that's not really a good reason. – Henri Menke Sep 05 '17 at 04:12
  • with the added advantage of try_emplace i would always prefers this over emplace – Hariom Singh Sep 05 '17 at 04:40
  • 14
    @HenriMenke: ``emplace()`` does check for duplicates, *after* construction of the ``value_type``. It is much less efficient if a duplicate is found, because it is required to perform an allocation (and deallocation in case of a duplicate). – Arne Vogel Sep 05 '17 at 08:51
  • 2
    It should also be noted `emplace` is supported by more containers with more general semantics (cf. requirements on different sorts of containers). So in generic code not necessarily depending on `std::map` or `std::unordered_map`, `emplace` has more flexibility over `try_emplace`. – FrankHB Dec 27 '18 at 01:26

3 Answers3

54

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.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Are you saying that `emplace` with `piecewise_construct` is always better than `try_emplace`? What is the point of `try_emplace` at all then? – PowerGamer Dec 19 '19 at 15:03
  • I don't think that is what Praetorian is saying. See the question for reasons to use `try_emplace`. – Carl G Feb 04 '20 at 00:54
22

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);
}
T.C.
  • 133,968
  • 17
  • 288
  • 421
6

I would always prefer try_emplace over emplace. A Crucial difference is that try_emplace will not construct the object associated with the key,if the key already exists.This will boost the performance in case objects of that type are expensive to create

For example the below code (Example from https://github.com/PacktPublishing/Cpp17-STL-Cookbook/blob/master/Chapter02/efficient_insert_or_reassign_to_map.cpp)

#include <iostream>
#include <functional>
#include <list>
#include <map>

using namespace std;

struct billionaire {
    string name;
    double dollars;
    string country;
};

int main()
{
    list<billionaire> billionaires {
        {"Bill Gates", 86.0, "USA"},
        {"Warren Buffet", 75.6, "USA"},
        {"Jeff Bezos", 72.8, "USA"},
        {"Amancio Ortega", 71.3, "Spain"},
        {"Mark Zuckerberg", 56.0, "USA"},
        {"Carlos Slim", 54.5, "Mexico"},
        // ...
        {"Bernard Arnault", 41.5, "France"},
        // ...
        {"Liliane Bettencourt", 39.5, "France"},
        // ...
        {"Wang Jianlin", 31.3, "China"},
        {"Li Ka-shing", 31.2, "Hong Kong"}
        // ...
    };

    map<string, pair<const billionaire, size_t>> m;

    for (const auto &b : billionaires) {
        auto [iterator, success] = m.try_emplace(b.country, b, 1);

        if (!success) {
            iterator->second.second += 1;
        }
    }


    for (const auto & [key, value] : m) {
        const auto &[b, count] = value;

        cout << b.country << " : " << count << " billionaires. Richest is "
        << b.name << " with " << b.dollars << " B$\n";
    }
}

For the above code

m.try_emplace(b.country, b, 1);

if there unsuccessful insertion the pairs will not get constructed which adds to the performance

Hariom Singh
  • 3,512
  • 6
  • 28
  • 52