Insertion into an unordered map changes the ptr share to null
In the example below, after extracting the value from the first map and inserting into the second map, the total ptr becomes null. Even the destructor is not called. I can't figure out what exactly is wrong.
#include <iostream>
#include <memory>
#include <unordered_map>
class Test
{
public:
Test(){}
~Test(){}
int test;
};
typedef std::shared_ptr<Test> Test_ptr;
typedef std::unordered_map<std::string, Test_ptr> TestMap;
int main()
{
TestMap map1, map2;
std::string key("abc");
Test_ptr ptr(new Test);
map1.insert(TestMap::value_type(key, ptr));
TestMap::iterator iter = map1.find(key);
if (iter != map1.end())
{
map2.insert(*iter);
if (iter->second == nullptr)
{
std::cout << "after insert the shared ptr becomes null" << std::endl;
}
}
}
g ++ -std = c ++ 11 testsharedptr.cpp -o testsharedptr
gcc version 4.8.1 (GCC)
source to share
I have not been able to reproduce the problem using GCC 4.9.2. However, I was able to reproduce it using GCC 4.8.1.
The main reason is a bug in the libstdc ++ implementation from the following std::unordered_map::insert()
overload
template< class P >
std::pair<iterator,bool> insert( P&& value );
GCC implementation 4.8.1
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)); }
and the implementation of GCC 4.9.2 is
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::forward<_Pair>(__x)); }
In the case of GCC 4.8.1, the portable map entry is moved to map2
instead of being copied. Thus, std::shared_ptr
from map1
gets its value nullptr
as a side effect of the move.
If possible, I would recommend moving to GCC 4.8.2 or newer, which has fixed this bug.
If you are unable to complete the upgrade, using const_iterator
will result in the expected behavior:
TestMap::const_iterator iter = map1.find(key);
By using const_iterator
, you call this overload:
std::pair<iterator,bool> insert( const value_type& value );
which does not change the passed value.
source to share