Reputation: 118510
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
Upvotes: 25
Views: 23902
Reputation: 2721
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
Upvotes: 1
Reputation: 118510
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.
This is not working as of GCC svn r165422 (4.6.0).
Upvotes: 6
Reputation: 28097
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.
Upvotes: 28