How can you emplace directly a mapped value into an unordered_map?

Viewed 273

Have I understood it correctly that the ::insert function for std::unordered_map takes a std::pair<'key', 'mapped_value'> and emplace simply takes:

first_arg is the key

second_arg is the mapped element

so:

#include <unordered_map>

struct SomeStruct
{
    SomeStruct(int, double, char) {};
};
int main()
{
    std::unordered_map<int, SomeStruct> mymap2;
    mymap2.emplace(6, SomeStruct{ 5, 0.0, 'c' });
}

This means that there's no such thing as building my mapped value (struct) in place, is there? If these are the arguments that 'emplace' takes then why does it have variadic arguments in the first place? With variadic arguments the first argument could have been the key and the remaining arguments could have been the constructor arguments for the class type to construct in place, perfectly forwarded, that would have been more efficient, and if I'm right then in this case emplace is not that different from 'insert', which would do a copy or move. However in the above example 'emplace' looks to be doing a copy or move construction anyway.

Have I understood these arguments correctly? If I'm right then it's strange that there would be variadic template arguments.

Edit: I checked in my program above the constructor for SomeStruct is called when creating the temporary and then the move constructor, I assume to move it into the bucket. Is there any way to build it in place the std::vector::emplace does?

1 Answers

emplace is variadic to deal with all overloads of the constructor offered by std::pair. In particular std::pair has a piecewise constructor. It takes a std::piecewise_construct_t as first paramter. It allows to forward paramters to the constructor of the mapped value as a tuple.

The example from cppreference:

#include <iostream>
#include <utility>
#include <string>
#include <unordered_map>
 
int main()
{
    std::unordered_map<std::string, std::string> m;

    // [...]
 
    // uses pair's template constructor
    m.emplace("d", "ddd");
 
    // uses pair's piecewise constructor
    m.emplace(std::piecewise_construct,
              std::forward_as_tuple("c"),
              std::forward_as_tuple(10, 'c'));
    // as of C++17, m.try_emplace("c", 10, 'c'); can be used
 
    for (const auto &p : m) {
        std::cout << p.first << " => " << p.second << '\n';
    }
}
Related