Why can't I use map's braces initialization with unique_ptr?
Edit: I use gcc 7.2 in c++17 mode.
I can create and insert into unordered map using make_pair and the [] operator.
std::unordered_map<std::string, std::unique_ptr<A>> map;
map.insert(std::make_pair("hello",std::make_unique<A>()));
map["foo"] = std::make_unique<A>();
But I can't figure out why it fails when using braces.
map.insert({"foo", std::make_unique<A>()}); // Error
error: use of deleted function ‘std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&)
Since C++11, associative container insertion requires either copy-costructible lvalues or move-insertable mutable rvalues. So, there's no problem invoking insert()
along with make_pair()
as you do: the latter constructs a pair<const char[],unique_ptr>
that is used to initialize a value_type
rvalue.
The problem with the brace initialization is that (until C++17) the value_type const& overload gets preferred (because there's no proper value_type&& overload), hence a copy is requested and an error is raised.
Indeed, note that both forms compile in gcc trunk and clang 5(with libc++) in c++17 mode.
More precisely, from C++11 on, the relevant insert semantic requirements read
[unord.req] If t is a non-const rvalue expression, value_type shall be MoveInsertable into X; otherwise, value_type shall be CopyInsertable into X.
is the actual members specification where things differ; until C++17, we have:
pair<iterator, bool> insert(const value_type& obj);
template <class P> pair<iterator, bool> insert(P&& obj);
...
where the generic overload is SFINAE constrained to std::is_constructible<value_type, P&&>::value
. Clearly, using a braced-init-list will select the const& overload here (note: there's also an initializer_list overload, but it does not apply to the OP case).
Instead, in C++17 we also have
pair<iterator, bool> insert(value_type&& obj);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With