Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ unique_ptr and map

Tags:

I'm trying to use the C++0x unique_ptr class inside a map like so:

// compile with `g++ main.cpp -std=gnu++0x`  #include <string.h>     #include <map> #include <memory>  using namespace std;  struct Foo {     char *str;         Foo(char const *str_): str(strdup(str_)) {} };  int main(void) {     typedef std::map<int, unique_ptr<Foo>> Bar;     Bar bar;     auto a = bar.insert(Bar::value_type(1, new Foo("one")));     return 0; } 

However GCC gives me the following error (shortened, I think this is the relevant part, please test on your own C++ compiler):

 main.cpp:19:   instantiated from here /usr/include/c++/4.4/bits/unique_ptr.h:214: error: deleted function ‘std::unique_ptr::unique_ptr(const std::unique_ptr&) [with _Tp = Foo, _Tp_Deleter = std::default_delete]’ /usr/include/c++/4.4/bits/stl_pair.h:68: error: used here 

I'm really not sure what I've done wrong, this works on MSVC. I have found very similar questions, that seem alike, however their solutions do not work for me.

 matt@stanley:/media/data/src/c++0x-test$ gcc --version gcc-4.4.real (Ubuntu 4.4.3-4ubuntu5) 4.4.3 
like image 854
Matt Joiner Avatar asked Oct 11 '10 12:10

Matt Joiner


People also ask

What is a unique_ptr?

std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.

Is unique_ptr thread safe?

No, it isn't thread-safe. Both threads can potentially move the work pointer with no explicit synchronization, so it's possible for both threads to get the same value, or both to get some invalid pointer ... it's undefined behaviour.

What does unique_ptr get do?

std::unique_ptr::getReturns the stored pointer. The stored pointer points to the object managed by the unique_ptr, if any, or to nullptr if the unique_ptr is empty.

What happens when you move a unique_ptr?

A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it.


2 Answers

First: Your class Foo is really a very bad approximation of std::string. I predict lots of memory leaks. (Search for "rule of three".)

Second: A pointer is not implicitly convertible to a unique_ptr object (for safety reasons). But the templated pair constructor requires the arguments to be implicitly convertible to the respective value types. GCC seems to allow this but it is a bug. You have to create the unique_ptr object manually. Unfortunately the C++0x draft lacks the following, very useful function template that eases the creation of temporary unique_ptr objects. It's also exception-safe:

template<class T, class...Args> std::unique_ptr<T> make_unique(Args&&... args) {     std::unique_ptr<T> ret (new T(std::forward<Args>(args)...));     return ret; } 

In addition, let's use the new emplace function instead of insert:

auto a = bar.emplace(1,make_unique<Foo>("one")); 

emplace forwards both arguments to the corresponding pair constructor.

The actual problem you witnessed is that the pair constructor tried to copy a unique_ptr even though such an object is only "movable". It seems that GCC's C++0x support is not yet good enough to handle this situation correctly. From what I can tell, my line from above should work according to the current standard draft (N3126).

Edit: I just tried the following as a workaround using GCC 4.5.1 in experimental C++0x mode:

map<int,unique_ptr<int> > themap; themap[42].reset(new int(1729)); 

This is also supposed to work in the upcoming standard but GCC rejects it as well. Looks like you have to wait for GCC to support unique_ptr in maps and multimaps.

like image 195
sellibitze Avatar answered Sep 28 '22 05:09

sellibitze


I don't believe it's possible to correctly use unique_ptr in a map::insert yet. Here's the GCC bug for it:

Bug 44436 - [C++0x] Implement insert(&&) and emplace* in associative and unordered containers

It looks like it may be fixed for GCC 4.6, but it won't build from SVN on vanilla Ubuntu 10.04.1 to confirm.

Update0

This is not working as of GCC svn r165422 (4.6.0).

like image 34
Matt Joiner Avatar answered Sep 28 '22 04:09

Matt Joiner