Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

emplace unordered_set in unordered_map

How can I add a (statically defined) unordered_set to an unordered_map, without having to copy the unordered_set?

I tried this:

std::unordered_map<int, std::unordered_set<std::string>> my_map;
for (int i=0; i<100; i++)
  my_map.emplace(i, {"foo", "bar"});

and this:

std::unordered_map<int, std::unordered_set<std::string>> my_map;
for (int i=0; i<100; i++)
  my_map.insert(i, std::move(std::unordered_set<std::string>({"foo", "bar"})));

but none of them compiles, I get these errors (respectively):

error: no matching function for call to ‘std::unordered_map<int, std::unordered_set<std::basic_string<char> > >::emplace(int&, <brace-enclosed initializer list>)’

and

error: no matching function for call to ‘std::unordered_map<int, std::unordered_set<std::basic_string<char> > >::insert(int&, std::remove_reference<std::unordered_set<std::basic_string<char> > >::type)’
like image 681
Valentin Lorentz Avatar asked Dec 08 '22 02:12

Valentin Lorentz


2 Answers

Braced initializers are one of the edge cases that perfect forwarding is not so perfect about.

The issue is that braced initializers passed to function template parameters are in a non-deduced context and compilers are not allowed to deduce a type for them.

Luckily, the fix is pretty easy: just be explicit about the use of std::initializer_list.

my_map.emplace(i, std::initializer_list<std::string>{"foo", "bar"});

The usual way to solve this issue is by doing something like:

auto list = { "foo", "bar" };
my_map.emplace(i, list);

But this doesn't work for std::strings because decltype(list) is deduced as std::initializer_list<const char*>.

like image 158
TartanLlama Avatar answered Dec 24 '22 05:12

TartanLlama


The elements of maps (both map and unordered_map) are of type using value type = std::pair<key_t, mapped_type>. Therefore, emplace does not pass its arguments to the unordered_set<string> constructor!

Once you realize that, the solution is easy:

std::unordered_map<int, std::unordered_set<std::string>> my_map;
for (int i=0; i<100; i++)
    my_map.emplace(i, std::unordered_set<std::string>{"foo", "bar"});
like image 37
gha.st Avatar answered Dec 24 '22 05:12

gha.st