67

The C++11 std::map<K,V> type has an emplace function, as do many other containers.

std::map<int,std::string> m;

std::string val {"hello"};

m.emplace(1, val);

This code works as advertised, emplacing the std::pair<K,V> directly, however it results in a copy of key and val taking place.

Is it possible to emplace the value type directly into the map as well? Can we do better than moving the arguments in the call to emplace?


Here's a more thorough example:

struct Foo
{
   Foo(double d, string s) {}
   Foo(const Foo&) = delete;
   Foo(Foo&&) = delete;
}

map<int,Foo> m;
m.emplace(1, 2.3, string("hello")); // invalid
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • @Cheersandhth.-Alf, I've read a bunch of documents and come to the conclusion that it's not possible. However I am not the most experienced C++ developer so wanted to run it past some of the gurus here, in the hope that I am wrong. Can you help? – Drew Noakes Jan 15 '15 at 09:41
  • okay, read http://en.cppreference.com/w/cpp/container/map/emplace – Cheers and hth. - Alf Jan 15 '15 at 09:42
  • 4
    Do you mean an *extra* copy? `val` is an lvalue, so a copy will have to be made at some point. – juanchopanza Jan 15 '15 at 09:42
  • 2
    @juanchopanza, imagine the value was some more complex type and that it was neither copyable nor moveable (and so of course cannot be introduced as an lvalue, nor moved/cast to an rvalue reference). I want to provide the value's constructor arguments directly to some kind of `emplace` function so that it is instantiated using perfect forwarding and (presumably) placement new within the map. – Drew Noakes Jan 15 '15 at 09:45
  • @juanchopanza, I've added a further example. – Drew Noakes Jan 15 '15 at 09:48
  • See http://en.cppreference.com/w/cpp/container/map/emplace for an example :) – Pixelchemist Jan 15 '15 at 09:51
  • @Pixelchemist, naturally I had RTFM, but without knowing what `piecewise_construct` and `forward_as_tuple` do, it wasn't apparent that they addressed my issue. Reading the docs on `pair` that Praetorian linked to offered the missing piece (pun intended). – Drew Noakes Jan 15 '15 at 09:55
  • If I remember correctly, the `emplace` functionality of `map`, and all other unique-key containers, is actually slower than the regular insert. For some reason, the standard demands (I don't have a quote, but this is what Scott Meyers showed at a conference) that the new object is first allocated and moved before it is checked that the key doesn't exist yet. If it does already exist, the object is destroyed again. Bottomline: unless I am proved wrong, don't use emplace :-) – JorenHeit Jan 15 '15 at 12:26
  • 1
    I found it again on youtube: https://www.youtube.com/watch?v=smqT9Io_bKo#t=46m00s – JorenHeit Jan 15 '15 at 12:39
  • @JorenHeit, thanks for the link. In my case the key is guaranteed to be unique upon insert (emplace) so I'll keep the solution given. – Drew Noakes Jan 15 '15 at 12:39
  • 4
    [Important note!](http://stackoverflow.com/questions/20830349/calling-stdmapemplace-and-avoiding-unnecessary-constructions#comment31241611_20830492) – Lightness Races in Orbit Jan 15 '15 at 15:06
  • As the constructor could throw, it needs a strong exception guarantee, but so does the regular insert so I cannot see how that would be any slower. – CashCow Oct 14 '16 at 07:31
  • What makes this code invalid for g++5.4 (it tries to use the delete copyconstructor) but valid for versions above ? – Benoît Jun 26 '17 at 11:50
  • But your second example is rather strange. It never had a chance to work regardless of the availability of copy or move constructors. The arguments of `emplace` are forwarded to the constructor of `std::pair`. And `std::pair` in this map does not have a constructor that would accept `(1, 2.3, string("hello"))`. So, the code is invalid in any case. Your call to `emplace` should've looked like `m.emplace(1, Foo{ 2.3, std::string("hello") });`. That would indeed correctly illustrate the issue. – AnT stands with Russia Sep 11 '22 at 19:18

2 Answers2

84

The arguments you pass to map::emplace get forwarded to the constructor of map::value_type, which is pair<const Key, Value>. So you can use the piecewise construction constructor of std::pair to avoid intermediate copies and moves.

std::map<int, Foo> m;

m.emplace(std::piecewise_construct,
          std::forward_as_tuple(1),
          std::forward_as_tuple(2.3, "hello"));

Live demo

Praetorian
  • 106,671
  • 19
  • 240
  • 328
36

In C++17 this can more easily be achieved with the try_emplace method.

map<int,Foo> m;
m.try_emplace(1, 2.3, "hello");

This addition to the standard library was covered in paper N4279 and should already be supported in Visual Studio 2015, GCC 6.1 and LLVM 3.7 (the libc++ library).

James Holderness
  • 22,721
  • 2
  • 40
  • 52
  • 3
    Yes, `try_emplace()` is really the best solution. In particular, `emplace()` always constructs a Key-Value pair on the heap. So, if the Key is actually found in the table, `emplace()` will delete that just newly constructed Key-Value pair again. `try_emplace` on the contrary does everything in the expected order: Check, if they Key exists, and if yes, return an iterator to that Key-Value pair. If not, then it emplaces the new Key and Value into the container. – Kai Petzke Sep 12 '19 at 00:06
  • Well, simple `.emplace()` is the only option if *the key* has to be constructed in-place, and has no drawback if you *know* it is not yet present in the map. – Deduplicator Dec 27 '21 at 16:44