c++ std::unique_ptr won't compile in map

10,812

Solution 1

The error is because somewhere in the code, map wants to copy a std::pair<int, std::unique_ptr<Entity>>, however there is no copy constructor capable of this, because unique_ptr's are not copy constructable. This is specifically impossible to prevent multiple pointers owning the same memory.

So before std::move, there was no way to use an uncopiable element.

There are some solutions here.

However, in c++11 Map can make use of std::move to work with non-copyable values.

This is done by providing another insert operator, which is overloaded to include this signature:

template< class P > std::pair<iterator,bool> insert( P&& value );

This means an rvalue of a class that can be turned into a value_type can be used as an argument. The old insert is still available:

std::pair<iterator,bool> insert( const value_type& value );

This insert actually copies a value_type, which would cause an error since value_type is not copy constructable.

I think the compiler is selecting the non-templated overload, which causes the compilation error. Because it is not a template, it's failure is an error. On gcc at least, the other insert, which uses std::move, is valid.

Here is test code to see if your compiler is supporting this correctly:

#include <iostream>
#include <memory>
#include <utility>
#include <type_traits>

class Foo {
};

using namespace std;

int main() {
    cout << is_constructible<pair<const int,unique_ptr<Foo> >, pair<const int,unique_ptr<Foo> >& >::value << '\n';
    cout << is_constructible<pair<const int,unique_ptr<Foo> >, pair<const int,unique_ptr<Foo> >&& >::value << '\n';
}

The first line will output 0, because copy construction is invalid. The second line will output 1 since the move construction is valid.

This code:

map.insert(std::move(EntityMap::value_type(entity->getId(), std::move(entity))));

should call the move insert overload.

This code:

map.insert<EntityMap::value_type>(EntityMap::value_type(entity->getId(), std::move(entity))));

Really should call it.

EDIT: the mystery continues, vc returns the incorrect 11 for the test...

Solution 2

Your code works with the following:

int main() {
    EntityManager em;
    em.addEntity(std::unique_ptr<Entity>(new Entity(1)));

    return 0;
}

However this is cumbersome and I'd recommend defining addEntity like so:

void EntityManager::addEntity(Entity *entity) {
    if (entity == nullptr) 
        return;
    }

    map.insert(EntityMap::value_type(entity->getId(),
                std::unique_ptr<Entity>(entity)));
}

and inserting with

em.addEntity(new Entity(...));
Share:
10,812
Tips48
Author by

Tips48

Love sports (Especially Soccer) Know Java, and a little PHP MySQL JavaScript and JQuery Coded for Bukkit for a long time Love video games

Updated on June 05, 2022

Comments

  • Tips48
    Tips48 almost 2 years

    I'm currently trying to store a std::unique_ptr in a std::unordered_map, but I get a weird compile error. Relevant code:

    #pragma once
    
    #include "Entity.h"
    
    #include <map>
    #include <memory>
    
    class EntityManager {
    private:
        typedef std::unique_ptr<Entity> EntityPtr;
        typedef std::map<int, EntityPtr> EntityMap;
    
        EntityMap map;
    public:
    
        /*
        Adds an Entity
        */
        void addEntity(EntityPtr);
    
        /*
        Removes an Entity by its ID
        */
        void removeEntity(int id) {
            map.erase(id);
        }
    
        Entity& getById(int id) {
            return *map[id];
        }
    };
    
    void EntityManager::addEntity(EntityPtr entity) {
        if (!entity.get()) {
            return;
        }
    
        map.insert(EntityMap::value_type(entity->getId(), std::move(entity)));
    }
    

    This is the compile error:

    c:\program files (x86)\microsoft visual studio 12.0\vc\include\tuple(438): error C2280: 'std::unique_ptr<Entity,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a deleted function
    1>          with
    1>          [
    1>              _Ty=Entity
    1>          ]
    1>          c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory(1486) : see declaration of 'std::unique_ptr<Entity,std::default_delete<_Ty>>::unique_ptr'
    1>          with
    1>          [
    1>              _Ty=Entity
    1>          ]
    1>          This diagnostic occurred in the compiler generated function 'std::pair<const _Kty,_Ty>::pair(const std::pair<const _Kty,_Ty> &)'
    1>          with
    1>          [
    1>              _Kty=int
    1>  ,            _Ty=EntityManager::EntityPtr
    1>          ]