C++ inserting unique_ptr in map
As a first remark, I wouldn't call it ObjectArray
if it is a map and not an array.
Anyway, you can insert objects this way:
ObjectArray myMap;
myMap.insert(std::make_pair(0, std::unique_ptr<Class1>(new Class1())));
Or this way:
ObjectArray myMap;
myMap[0] = std::unique_ptr<Class1>(new Class1());
The difference between the two forms is that the former will fail if the key 0
is already present in the map, while the second one will overwrite its value with the new one.
In C++14, you may want to use std::make_unique()
instead of constructing the unique_ptr
from a new
expression. For instance:
myMap[0] = std::make_unique<Class1>();
std::unique_ptr with std::map
return _cache.rbegin()->second.get();
does not do what you want, as std::map
does not append elements but sorts them. However emplace
returns an iterator to what it just inserted, so you only need:
return _cache.emplace(foo, std::make_unique<Bar>(bar))->first->second.get();
Or even, since you don't actually need to store and copy the Bar
, and you can also sacrifice foo
:
return _cache.emplace(
std::move(foo),
std::make_unique<Bar>(ThirdPartLibrary::CreateBar())
)->first->second.get();
I'd also personally flip the (it == _cache.end())
condition to make it an early return, but that's just a matter of taste.
Otherwise, what you have looks good to me.
Compile error trying to put a unique_ptr into a map
You cannot copy a unique_ptr
, because then it will not be unique. You have to move it - AddItem(std::move(someItem));
and myMap.emplace("test", std::move(item));
.
c++ map insert fails to compile when inserting an object that contains a unique_ptr
Struct1(Struct1& other)
As per language standard, it's the copy constructor; what you did here (by typo or on purpose, I don't know) is a auto_ptr
-style neither-move-nor-copy-semantics. Though with that done, the copy assignment is auto-generated, while the move operations are completely removed (unless defined manually).
Didn't you want that instead?
#include <utility>
#include <string>
#include <iostream>
#include <map>
#include <memory>
struct Struct1
{
Struct1(std::unique_ptr<std::string> pStr)
: pStr_(std::move(pStr)){}
~Struct1(){}
Struct1(Struct1&& other) //double! ampersand
: pStr_(std::move(other.pStr_)){}
std::unique_ptr<std::string> pStr_;
};
int main () {
std::string s("key");
std::unique_ptr<std::string> pStr = std::make_unique<std::string>("Hello");
std::map<std::string, Struct1>list;
Struct1 ste(std::move(pStr));
std::pair<std::string, Struct1> p{s,std::move(ste)}; //note the move
list.insert(std::move(p)); //and here
}
https://godbolt.org/z/fs44j69ja
C++ unique_ptr and map
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).
How do you add abstract base class unique_ptrs to a map?
The first cause of an error message is that a class type can never be used as the type for a dynamic_cast
. The target type of dynamic_cast
must always be either a pointer to class type (meaning the result is null if the cast fails) or a reference to class type (meaning to throw an exception if the cast fails).
So improvement #1:
my_map[0] = dynamic_cast<GameObject*>(std::unique_ptr<Enemy>().get());
But this won't work because GameObject
is a private base class of Enemy
. You probably meant to use public inheritance, but (when using class
instead of struct
) you must say so:
class Enemy : public GameObject
// ...
Next we'll find that the =
within the map statement is invalid. The left side has type std::unique_ptr<GameObject>
, which does not have any operator=
that can take a GameObject*
pointer. But it does have a reset
member for setting a raw pointer:
my_map[0].reset(dynamic_cast<GameObject*>(std::unique_ptr<Enemy>().get()));
Now the statement should compile - but it's still wrong.
Before getting to why it's wrong, we can make a simplification. dynamic_cast
is needed for getting a pointer to derived class from a pointer to base class, or for many other type changes within a more complicated inheritance tree. But it's not needed at all to get a pointer to base class from a pointer to derived class: this is a valid implicit conversion, since every object with a derived class type must always contain a subobject of the base class type, and there's no "failure" case. So the dynamic_cast
here can just be dropped.
my_map[0].reset(std::unique_ptr<Enemy>().get());
The next problem is that std::unique_ptr<Enemy>()
creates a null unique_ptr
, and no Enemy
object is created at all. To create an actual Enemy
, we can write instead either std::unique_ptr<Enemy>(new Enemy)
or std::make_unique<Enemy>()
.
my_map[0].reset(std::make_unique<Enemy>().get());
Still wrong, and in a slightly tricky way. Now the problem is that the created Enemy
object is owned by the temporary std::unique_ptr<Enemy>
object returned by make_unique
. The reset
tells the std::unique_ptr<GameObject>
within the map that it should own a pointer to the same object. But at the end of the statement, the temporary std::unique_ptr<Enemy>
gets destroyed, and it destroys the Enemy
object. So the map is left with a pointer to a dead object, which is invalid to use - not what you wanted.
But the solution here is that we don't need to mess around with get()
and reset()
at all. There is an operator=
that allows assigning an rvalue std::unique_ptr<Enemy>
to a std::unique_ptr<GameObject>
, and it does the right thing here. It makes use of the implicit conversion from Enemy*
to GameObject*
.
my_map[0] = std::make_unique<Enemy>();
(Note if you had a named std::unique_ptr<Enemy>
, you would need to std::move
it to allow the assignment, as in my_map[0] = std::move(enemy_ptr);
. But std::move
is not needed above because the result of make_unique
is already an rvalue.)
Now this statement is much shorter and more legible, and will actually do what you want.
A comment also suggested the possibility
my_map.emplace(0, std::make_unique<Enemy>());
This is also valid, but there's a possibly important difference: if the map already has an object with key zero, the =
version will destroy and replace the old one, but the emplace
version will leave the map alone and the just-created Enemy
will be destroyed instead.
Insert unique_ptr into map, pointer gets destroyed
I don't think your pointer is getting destroyed. What you are seeing here:
template <class T>
void insertOperand(std::string &s, T o = T()) {
op.insert(std::pair<std::string, std::unique_ptr<StreamOperand>>(
s, std::move(std::unique_ptr<T>(new T(o)))
);
}
is the destruction of o
, after it has been used to construct the heap allocated T
used in the unique_ptr
.
The map being empty is not a symptom of the pointer being destroyed. If this were the case (the pointer being destroyed), you would have an entry in the map for a given key, with an invalid unique_ptr.
why does C++ give me an error when inserting unique pointer into unordered map?
This:
m_Tests.emplace(testName, test);
will try to copy test
, because (a) it is an lvalue expression, (b) expressions never have reference type, and (c) emplace
does not take an lvalue reference.
unique_ptr
s cannot be copied.
This is what std::move
is for!
m_Tests.emplace(testName, std::move(test));
Now you have an rvalue expression, which is perfectly suited to bind to the rvalue reference that emplace
does take.
I'm actually slightly slightly lying here, and things are a bit more complicated since emplace
is variadic. But whatever. The important thing is that you're creating another link in the chain of ownership for this unique_ptr
, ultimately passing ownership on to the map, and std::move
is the way to do that.
Related Topics
How to Get Current CPU and Ram Usage in C++
How to Cast Simple Pointer to a Multidimensional-Array of Fixed Size
What Could C/C++ "Lose" If They Defined a Standard Abi
Do C++11 Regular Expressions Work with Utf-8 Strings
C++ Concatenate Two Int Arrays into One Larger Array
How to Parallelize a for Loop Through a C++ Std::List Using Openmp
Do Static Members of a Class Occupy Memory If No Object of That Class Is Created
C++: How to Get Fprintf Results as a Std::String W/O Sprintf
Lambda Capture and Parameter with Same Name - Who Shadows the Other? (Clang VS Gcc)
Is There Some Ninja Trick to Make a Variable Constant After Its Declaration
Weird Msc 8.0 Error: "The Value of Esp Was Not Properly Saved Across a Function Call..."
Library Is Linked But Reference Is Undefined
How to Check If a Key Is Pressed on C++
Do Stl Iterators Guarantee Validity After Collection Was Changed