Reputation: 2010
I have a std::map
of std::string
and std::unique_ptr<BaseInt>
. Basically I want to have a map with the class name as string key
and a unique pointer as the corresponding value
. And access the pointer as map["Derived1"]
etc (explained in code below).
When I iterate over the std::map
and try to push each value
to a std::vector
, I see the following error
Error C2280 '
std::unique_ptr<BaseInt,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)
': attempting to reference a deleted function CreateInstanceFromList c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\xmemory
I am on Visual Studio 2017 Version 15.9.16 MSVC 14.16.27023
The implementation code is as follows. BaseInt
is a BaseClass with an int
member and a pure virtual replaceInt()
. DerivedInt1
and DerivedInt2
implement the virtual function with different int
values and differ by a parameter in their construction.
#include "UserClass.h"
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <memory>
#include <typeinfo>
typedef std::vector<std::unique_ptr<BaseInt>> vec_type;
typedef std::map<std::string, std::unique_ptr<BaseInt>> map_type;
template<typename T> std::unique_ptr<T> createInstance(vec_type& vec) { return std::make_unique<T>(); };
template<typename T, typename U> std::unique_ptr<T> createInstance(vec_type& vec, U u) { return std::make_unique<T>(u); };
void fillVector(map_type& map)
{
vec_type my_vec;
for (auto const& it : map )
{
std::cout << it.first << std::endl;
it.second->replaceInt();
//my_vec.emplace_back(std::move(it.second)); //this line gives error
}
// idea is to be able to access the pointer as map["Derived1"]
std::cout << my_vec.size() << std::endl;
}
int main()
{
map_type my_map;
my_map.emplace("Derived1", createInstance<DerivedInt1>(my_vec, 7));
my_map.emplace("Derived2", createInstance<DerivedInt2>(my_vec));
fillVector(my_map);
return 0;
}
My intuition is somehow I am trying to call the copyconstructor of the unique_ptr
but I don't actually see how. Thanks.
So, the main problem is the const&
iterator as @ALX23z mentioned in the answer. The following change works:
for (auto it = map.begin(); it != map.end(); ++it )
{
std::cout << it->first << std::endl;
it->second->replaceInt();
my_vec.emplace_back(std::move(it->second));
}
As pointed out by multiple people, I made a basic design flaw of not using unique_ptr
as unique. I can see the problem you mentioned and I will look into the possibility of changing to a shared_ptr
or modifying the design. Thanks for all the quick response. I will update my changes here tomorrow.
After looking at the main project scenarios, I can see that the std::map
here is really just a one-time use container, so I can keep it local and pass all ownership to the std::vector
.
Someone downvoted the question and flagged to close it mentioning
Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers. See: How to create a Minimal, Reproducible Example.
I really would like them to enlighten me on how this flagging was deemed necessary. I totally presented a short code necessary to reproduce the exact problem I was facing; instead of the huge project code I am actually working on. I provided all information necessary with even my intuition of where it was failing. I added multiple edit information so that any reader can find it useful. Atleast, leave a comment and explain what can be improved. Sorry for the rant.
Upvotes: 0
Views: 491
Reputation: 79780
std::unique_ptr
is the owner of the pointer, it'll delete the data when you destroy the std::unique_ptr
.
So you don't want to have two std::unique_ptr
's to point to the same data because when one of them deletes your data the other one ends up pointing to something which is no longer your data. You could call std::unique_ptr<>::release()
before deleting the std::unique_ptr
but then it sounds like you are not using the std::unique_ptr
correctly if you want two "unique pointers".
You could use std::shared_ptr
. Or, what I usually do, decide who owns the data, vector or map, then the owner stores std::unique_ptr
's to data and the other stores just plain pointers. You have to make sure you deal with all the pointers before you delete the std::unique_ptr
.
So for example:
typedef std::vector< BaseInt * > vec_type;
my_vec.push_back(it.second.get());
I haven't tried compiling but std::unique_ptr<>::get()
returns raw pointer that is still managed by the std::unique_ptr
in the map. Then remove from vector before removing from map, because removing from map will delete data pointed to by std::unique_ptr
.
EDIT:
When you move the std::unique_ptr
the ownership is transfered to the new std::unique_ptr
so the old one no longer holds valid pointer, after you move the pointer from map to vector the map no longer has the pointer to your data:
my_vec.emplace_back(std::move(it->second));
Check what you have in map after you move pointers to vector.
Upvotes: 3
Reputation: 4713
The line gives error because you iterate over the map via a const iterator. To apply emplace_back
on you need a non-const reference.
Also you have an error with your approach in general. unique_ptr
is a unique pointer. You cannot have more than one unique_ptr
pointing to the same object. So you cannot share them between map and a vector.
Use 2 shared_ptr, or unique_ptr with a raw pointer depending on requirements.
Upvotes: 3