The common advice is to prefer using std::map::try_emplace
in preference to std::map::emplace
in almost every instance.
I wrote a simple test to trace object creation/copying/moving/destruction when calling those functions, with and without clashes, and the results show that try_emplace
incurs an extra move and destruction of the key when it is not already in the map.
Why the difference in behaviour?
I do know that moves and destructions of moved-from objects are usually cheap, especially so for trivial keys, but I was still surprised by the results as they seem to imply that for some cases emplace
might be more efficient.
Compiler explorer link (Clang 14, libc++, -O3)
Source:
#include <map>
#include <iostream>
struct F {
F(int i): i(i) { std::cout << "- ctor (" << i << ")\n"; }
~F() { std::cout << "- dtor (" << i << ")\n"; }
F(const F& f): i(f.i) { std::cout << "- copy ctor (" << i << ")\n"; }
F(F&& f): i(f.i) { std::cout << "- move ctor (" << i << ")\n"; }
F& operator=(const F& f) { i = f.i; std::cout << "- copy (" << i << ")\n"; return *this; }
F& operator=(F&& f) { i = f.i; std::cout << "- move (" << i << ")\n"; return *this; }
bool operator <(const F& f) const { return i < f.i; }
int i{};
};
int main() {
std::map<F, F> m;
std::cout << "emplace 1:\n";
m.emplace(1, 2);
std::cout << "emplace 2:\n";
m.emplace(1, 3);
std::cout << "clear:\n";
m.clear();
std::cout << "try_emplace 1:\n";
m.try_emplace(1, 2);
std::cout << "try_emplace 2:\n";
m.try_emplace(1, 3);
std::cout << "done:\n";
}
Results:
emplace 1:
- ctor (1)
- ctor (2)
emplace 2:
- ctor (1)
- ctor (3)
- dtor (3)
- dtor (1)
clear:
- dtor (2)
- dtor (1)
try_emplace 1:
- ctor (1)
- move ctor (1)
- ctor (2)
- dtor (1)
try_emplace 2:
- ctor (1)
- dtor (1)
done:
- dtor (2)
- dtor (1)