Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Construct unordered_map with a value_type of unique_ptr

This snippet of code doesn't seem to work because the unique pointer gets stored into a pair object and then is attempted to be copied from it. Can this be avoided?

std::unordered_map<std::string,std::unique_ptr<int>> _map {
    {"hello", std::make_unique<int>(7)}
};

A full code example and compilation error can be seen here http://cpp.sh/7uc3a

like image 424
Keltek Avatar asked Sep 23 '19 23:09

Keltek


1 Answers

Yes, by not using list-initialization.

std::unordered_map<std::string,std::unique_ptr<int>> _map;
_map.insert({"hello", std::make_unique<int>(7)});

std::unordered_map needs to call either the copy or move constructor in order to store any keys and values in its internal buffer. In the case of std::unique_ptr, the copy constructor is deleted, since making a copy of a std::unique_ptr would make it, well, not unique at all. That leaves only the move constructor. The problem is with how you're initializing your map:

std::unordered_map<std::string,std::unique_ptr<int>> _map {
    {"hello", std::make_unique<int>(7)}
};

You're initializing your map's values using a std::initializer_list.

From cppreference:

An object of type std::initializer_list is a lightweight proxy object that provides access to an array of objects of type const T.

Effectively, all of the values in your initializer list are const. That means the move constructor on your std::unique_ptr can't be called, since moving an object generally requires modifying the original instance. Since the std::unique_ptr can't be moved, the compiler falls back on the copy constructor, which is why it ends up complaining that the copy constructor is deleted.

The snippet I posted works because insert accepts a non-const std::pair, which means that the std::unique_ptr is non-const, and can have its move constructor called.

like image 133
Caleb Rush Avatar answered Oct 01 '22 10:10

Caleb Rush