c++ std::unique_ptr 不会在 map 中编译

标签 c++ visual-studio-2012 c++11 unique-ptr

我目前正在尝试将 std::unique_ptr 存储在 std::unordered_map 中,但出现奇怪的编译错误。 相关代码:

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

这是编译错误:

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>          ]

最佳答案

错误是因为在代码的某处,map 想要复制一个 std::pair<int, std::unique_ptr<Entity>> ,但是没有复制构造函数可以做到这一点,因为 unique_ptr 不是可复制构造的。这尤其不可能阻止多个指针拥有相同的内存。

所以在 std::move 之前,没有办法使用不可复制的元素。

有一些解决方案here .

但是,在 c++11 中,Map 可以使用 std::move 来处理不可复制的值。

这是通过提供另一个插入运算符来完成的,该运算符被重载以包含此签名:

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

这意味着可以将可以转换为 value_type 的类的右值用作参数。旧插页仍然可用:

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

此插入实际上复制了一个 value_type,这将导致错误,因为 value_type 不可复制构造。

我认为编译器正在选择非模板重载,这会导致编译错误。因为它不是模板,所以它的失败是一个错误。至少在 gcc 上,另一个使用 std::move 的插入是有效的。

这里是测试代码,看看你的编译器是否正确支持:

#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';
}

第一行会输出0,因为复制构造无效。第二行将输出 1,因为移动构造有效。

这段代码:

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

应该调用移动插入重载。

这段代码:

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

真的应该叫它。

编辑:谜团还在继续,vc 为测试返回了不正确的 11...

关于c++ std::unique_ptr 不会在 map 中编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21056872/

相关文章:

TFS 回滚到变更集

c++ - 内联命名空间是否必须始终如此限定?

c++ - 这可能是一个天真的问题,但简单来说如何实现与 std::vector 相同的复制构造函数?

c# - C# 或 C++ 中的音频处理

c++ - 任何用于 RPC over pipes/internal linux sockets 的开源 C/C++ 库/框架?

c# - 来自 IIS7 的 MSSQL 连接

c++ - 如何使用 decltype 获取某些类类型的成员函数的地址?

c++ - 在 C++ 中双重重载 istream 和 ostream 运算符

c++ - 从 C++ 代码运行/Shell/Fork 程序 + 从基本类型构造字符串

c# - 为什么将 Visual Studio 作为 "Run as Administrator"运行?