25

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
4
  • 1
    Not the answer, but Foo should have a destructor that frees str because strdup mallocs memory and copies str_ into that memory. Commented Oct 11, 2010 at 12:55
  • 5
    It's C++0x no matter when it is officially published. As Stroustrup put it, think of x as a hexadecimal :) Commented Oct 11, 2010 at 12:58
  • @Loy Franco: Yeah I know, that would be next to implement, but I never made it this far (it's a simplified extract of a larger problem I'm having). Commented Oct 11, 2010 at 13:05
  • You should add a destructor Foo::~Foo that frees the memory and explicitly disable any copying. Commented Oct 11, 2010 at 14:06

4 Answers 4

28

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.

Sign up to request clarification or add additional context in comments.

4 Comments

@Matt: Well, there's nothing I can do about it. std::unique_ptr as mapped type in a map is supposed to work. GCC can't handle that yet. You could file a bug report.
Thanks for a great answer. When support is added in GCC, I'll be able to test it and give +1!
I just ran into this as well. Thanks for the detailed write-up. Saved me a lot of head-scratching.
this seems to be a problem in Clang 3.6.2 as well
6

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).

1 Comment

The code in question works with GCC 4.6.1 with a minor change (added an explicit conversion to unique_ptr<Foo>).
1

I've just been tinkering with unique_ptr in gcc 4.6.0. Here's some (very ugly) code which works correctly:

std::map<int, std::unique_ptr<int> > table;
table.insert(std::pair<int, std::unique_ptr<int>>(15, std::unique_ptr<int>(new int(42))));
table[2] = std::move(table[15]);
for (auto& i : table)
{
    if (i.second.get())
        printf("%d\n", *i.second);
    else
    {
        printf("empty\n");
        i.second.reset(new int(12));
    }
}
printf("%d\n", *table[15]);

The output is 42, empty, 12 - so apparently it works. As far as I can tell, the only 'problem' is that awful looking insert command. 'emplace' isn't implemented in gcc 4.6.0

1 Comment

+1 At least insert and the pair's conversion constructor seems to be working as intended (conversion since value_type is pair<const int,....) But there is still slight issue w.r.t. exception safety. In general, dynamically allocating an object and initializing a unique_ptr with that pointer should be consecutive actions. But C++ does not guarantee in which order subexpressions are evaluated. That's why I used make_unique as a function call. This way, there are two extra sequence points.
0

Try using emplace method of map.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.