Is it intended by the C++ standards committee that in C++11 unordered_map destroys what it inserts?
As others have pointed out in the comments, the "universal" constructor is not, in fact, supposed to always move from its argument. It's supposed to move if the argument is really an rvalue, and copy if it's an lvalue.
The behaviour, you observe, which always moves, is a bug in libstdc++, which is now fixed according to a comment on the question. For those curious, I took a look at the g++-4.8 headers.
bits/stl_map.h
, lines 598-603
template<typename _Pair, typename = typename
std::enable_if<std::is_constructible<value_type,
_Pair&&>::value>::type>
std::pair<iterator, bool>
insert(_Pair&& __x)
{ return _M_t._M_insert_unique(std::forward<_Pair>(__x)); }
bits/unordered_map.h
, lines 365-370
template<typename _Pair, typename = typename
std::enable_if<std::is_constructible<value_type,
_Pair&&>::value>::type>
std::pair<iterator, bool>
insert(_Pair&& __x)
{ return _M_h.insert(std::move(__x)); }
The latter is incorrectly using std::move
where it should be using std::forward
.
template <class P> pair<iterator,bool> insert ( P&& val );
Which is a greedy universal reference move overload, consuming anything not matching any of the other overloads, and move constructing it into a value_type.
That is what some people call universal reference, but really is reference collapsing. In your case, where the argument is an lvalue of type pair<int,shared_ptr<int>>
it will not result in the argument being an rvalue reference and it should not move from it.
So why did our code above choose this overload, and not the unordered_map::value_type overload as probably most would expect?
Because you, as many other people before, misinterpreted the value_type
in the container. The value_type
of *map
(whether ordered or a unordered) is pair<const K, T>
, which in your case is pair<const int, shared_ptr<int>>
. The type not matching eliminates the overload that you might be expecting:
iterator insert(const_iterator hint, const value_type& obj);