3

I have a class containing a std::unique_ptr<> and I want to put instances of this class inside of an std::map<>. I thought one of the things that motivated the introduction of move semantics to C++ was the possibility of putting things like unique_ptrs inside standard containers (and that really works, in the case of vectors). But it seems to me that std::map<> doesn't like this idea. Why is this so?

#include <map>
#include <memory>

int main()
{
    std::map<int,std::unique_ptr<int>> map;

    // error on the line that follows (use of disabled lvalue copy constructor)
    map.insert(std::make_pair(1,std::unique_ptr<int>(new int(2))));

    return 0;
}

Thanks.

-- edit

Just to be more clear, the exact error messages are:

mingw32-g++.exe --std=gnu++0x   -ID:\CodeEnv\Libraries\Boost  -c D:\CodeEnv\CodeMess\Untitled1.cpp -o D:\CodeEnv\CodeMess\Untitled1.o
mingw32-g++.exe  -o D:\CodeEnv\CodeMess\Untitled1.exe D:\CodeEnv\CodeMess\Untitled1.o   
In file included from c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/stl_algobase.h:66,
                 from c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/stl_tree.h:62,
                 from c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/map:60,
                 from D:\CodeEnv\CodeMess\Untitled1.cpp:1:
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/unique_ptr.h: In copy constructor 'std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >::pair(const std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >&)':
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/stl_pair.h:68:   instantiated from 'std::_Rb_tree_node<_Val>::_Rb_tree_node(_Args&& ...) [with _Args = const std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >&, _Val = std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >]'
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/ext/new_allocator.h:111:   instantiated from 'void __gnu_cxx::new_allocator<_Tp>::construct(_Tp*, _Args&& ...) [with _Args = const std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >&, _Tp = std::_Rb_tree_node<std::pair<const int, std::unique_ptr<int, std::default_delete<int> > > >]'
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/stl_tree.h:394:   instantiated from 'std::_Rb_tree_node<_Val>* std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_create_node(_Args&& ...) [with _Args = const std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >&, _Key = int, _Val = std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >, _KeyOfValue = std::_Select1st<std::pair<const int, std::unique_ptr<int, std::default_delete<int> > > >, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, std::unique_ptr<int, std::default_delete<int> > > >]'
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/stl_tree.h:881:   instantiated from 'std::_Rb_tree_iterator<_Val> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_(const std::_Rb_tree_node_base*, const std::_Rb_tree_node_base*, const _Val&) [with _Key = int, _Val = std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >, _KeyOfValue = std::_Select1st<std::pair<const int, std::unique_ptr<int, std::default_delete<int> > > >, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, std::unique_ptr<int, std::default_delete<int> > > >]'
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/stl_tree.h:1177:   instantiated from 'std::pair<typename std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(const _Val&) [with _Key = int, _Val = std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >, _KeyOfValue = std::_Select1st<std::pair<const int, std::unique_ptr<int, std::default_delete<int> > > >, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, std::unique_ptr<int, std::default_delete<int> > > >]'
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/stl_map.h:500:   instantiated from 'std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const std::pair<const _Key, _Tp>&) [with _Key = int, _Tp = std::unique_ptr<int, std::default_delete<int> >, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, std::unique_ptr<int, std::default_delete<int> > > >]'
D:\CodeEnv\CodeMess\Untitled1.cpp:7:   instantiated from here
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/unique_ptr.h:214: error: deleted function 'std::unique_ptr<_Tp, _Tp_Deleter>::unique_ptr(const std::unique_ptr<_Tp, _Tp_Deleter>&) [with _Tp = int, _Tp_Deleter = std::default_delete<int>]'
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/stl_pair.h:68: error: used here
In file included from c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/map:60,
                 from D:\CodeEnv\CodeMess\Untitled1.cpp:1:
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/stl_tree.h: In constructor 'std::_Rb_tree_node<_Val>::_Rb_tree_node(_Args&& ...) [with _Args = const std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >&, _Val = std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >]':
c:\mingw\bin\../lib/gcc/mingw32/4.4.1/include/c++/bits/stl_tree.h:136: note: synthesized method 'std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >::pair(const std::pair<const int, std::unique_ptr<int, std::default_delete<int> > >&)' first required here 

It would look like that's a problem with std::pair<>, but it works perfectly fine when used alone:

int main()
{
    std::pair<int,std::unique_ptr<int>> pair;

    pair = std::make_pair(1,std::unique_ptr<int>(new int(2))); // no errors
    return 0;
}

Although that obviously doesn't mean it can't be misused:

int main()
{
    std::pair<int,std::unique_ptr<int>> pair1,pair2;

    pair1 = std::make_pair(1,std::unique_ptr<int>(new int(2))); // no errors
    pair2 = pair1; // BOOM ! Copy from lvalue
    return 0;
}

which probably is what happens inside of std::map<>.

Clues?

-- edit

Reviewing the error messages, it really happens to be a problem with TDM-GCC 4.4.1's std::map<> implementation. It doesn't seem to feature a move semantics-aware insertion method like std::vector<>::push_back(value_type&&).

What to do now?

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Gui Prá
  • 5,559
  • 4
  • 34
  • 59
  • 1
    on ubuntu, g++ 4.4.1 clearly says "error: ‘unique_ptr’ is not a member of ‘std’" – amit kumar Feb 20 '10 at 20:08
  • You have to compile your sources with --std=c++0x (or --std=gnu++0x, something like that) for unique_ptr to be available from ``. That's probably why you see that message. Here, unique_ptr is used fine. There is just this mess with std::map. – Gui Prá Feb 23 '10 at 04:30
  • If its `map` can't handle non-copyable types, it will not help if you only work-around this single problems. Sooner or later, you will get other problems elsewhere. – Johannes Schaub - litb Mar 02 '10 at 00:31
  • Suggestions? I bet I know that one: stop relying on standard drafts, wait for it to be standarized and for fully-compliant compilers to come? God, getting to know C++0x before it can be reliably used is masochism. I want it ready so baad... – Gui Prá Mar 03 '10 at 02:38

2 Answers2

2

I ran into the same problem. I was able to add the required functionality without too much effort by patching bits/stl_map.h and bits/stl_tree.h. To get your particular example to work, you basically just have to add an insert(value_type&&) method to the map class. It is essentially the same as insert(const value_type&), but using std::move() where appropriate. You have to add the right support methods as well, but you can use the compile errors to tell you what is needed.

This issue has been reported to the GCC team (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=44436) and someone is working on it, so hopefully we will see support for move constructibles in associative and unordered containers soon.

1

What compiler are you using? It compiles fine with VS10 beta 2.


Edit: Compiler in question is GCC

This appears to be a bug in the STL implementation, since GCC is open source you can submit a patch fixing this issue.
Luckily the fix is in the STL code and not the compiler code so it shouldn't be too difficult.

In the meantime you can enter your fix in the local pair/map header files.

Motti
  • 110,860
  • 49
  • 189
  • 262
  • By the way, it's actually a GCC 4.4.1 port for Windows (TDM-GCC: http://www.tdragon.net/recentgcc/). – Gui Prá Feb 18 '10 at 09:20
  • Haha. I've been doing just that right now. Failed miserably :] Actually I felt there would be too many things to patch so I gave up. I just wonder.. if it wasn't implemented in GCC and was in VC++, there might be a dark reason for this being so... Nevertheless, I've submitted a bug report to the TDM-GCC developers (here: http://sourceforge.net/tracker/?func=detail&aid=2954089&group_id=200665&atid=974439). Thanks. – Gui Prá Feb 18 '10 at 10:53
  • 1
    For the time being, I'll just use `boost::ptr_map<>` even though the objects could have been stack allocated. I'll change this when a solution pops and, if I find it out somewhere else, I'll answer this question so other people benefit. – Gui Prá Feb 18 '10 at 11:02