[英]c++ std::unique_ptr won't compile in map
我目前正在尝试将 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 之前,没有办法使用不可复制的元素。
有一些解决方案在这里。
但是,在 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 ......
您的代码适用于以下内容:
int main() {
EntityManager em;
em.addEntity(std::unique_ptr<Entity>(new Entity(1)));
return 0;
}
然而,这很麻烦,我建议像这样定义 addEntity:
void EntityManager::addEntity(Entity *entity) {
if (entity == nullptr)
return;
}
map.insert(EntityMap::value_type(entity->getId(),
std::unique_ptr<Entity>(entity)));
}
并插入
em.addEntity(new Entity(...));
不确定这个解决方案是否也能帮助你,但是当我从静态库切换到动态库时,我突然在私有std::map<int, std::unique_ptr<SomeType>>
数据成员上遇到了同样的错误Visual Studio 2015 (Update 2)
。
由于将模板数据成员与__declspec(dllexport)
一起使用会产生警告(至少在 MSVC 中),因此我通过(几乎)应用PIMPL
(私有实现)习语解决了该警告。 令人惊讶的是,C2280 错误也以这种方式消失了。
在您的情况下,它将是:
class EntityManagerPrivate {
public:
EntityMap map;
};
class EntityManager {
private:
EntityManagerPrivate* d; // This may NOT be a std::unique_ptr if this class
// shall be ready for being placed into a DLL
public:
EntityManager();
~EntityManager();
// ...
};
并在 .cpp 文件中:
EntityManager::EntityManager() :
d( new EntityManagerPrivate() )
{
}
EntityManager::~EntityManager()
{
delete d;
d = nullptr;
}
// in all other methods, access map by d->map
请注意,对于真正的PIMPL
您必须将私有类移动到仅由 .cpp 引用的自己的头文件中。 实际的标头只有一个前向声明class EntityManagerPrivate;
在包含之后。 对于真正的PIMPL
,私有类除了数据成员之外还必须有实现。
我在使用 msvc 14.15.26726 的 VS 2017 上遇到了同样的问题。 根据编译器错误日志,事情似乎与在实例化期间需要 std::pair<_kT, _T> 的复制构造函数有关。 我不知道为什么,但对我来说一个有趣的观察(和解决方法)是在地图声明之前放置一个 std::unique_ptr 声明,例如:
#pragma once
#include "Entity.h"
#include <map>
#include <memory>
class EntityManager {
private:
typedef std::unique_ptr<Entity> EntityPtr;
typedef std::map<int, EntityPtr> EntityMap;
std::unique_ptr<Entity> aDummyStub; //<-- add this line
EntityMap map;
//...
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.