c++c++11c++14

Error in pushing unique_ptrs from map into a vector


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.

Edit:

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));
    }

Edit 2:

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.

Edit 3:

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.


Solution

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