[英]c++ std::unique_ptr won't compile in map
I'm currently trying to store a std::unique_ptr in a std::unordered_map, but I get a weird compile error.我目前正在尝试将 std::unique_ptr 存储在 std::unordered_map 中,但出现奇怪的编译错误。 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> ]
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.错误是因为在代码中的某处, map 想要复制std::pair<int, std::unique_ptr<Entity>>
,但是没有能够做到这一点的复制构造函数,因为 unique_ptr 不可复制构造。 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.所以在 std::move 之前,没有办法使用不可复制的元素。
There are some solutions here .有一些解决方案在这里。
However, in c++11 Map can make use of std::move to work with non-copyable values.但是,在 c++11 中 Map 可以使用 std::move 来处理不可复制的值。
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.这意味着可以转换为 value_type 的类的右值可以用作参数。 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.这个插入实际上复制了一个 value_type,这会导致错误,因为 value_type 是不可复制构造的。
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.至少在 gcc 上,另一个使用 std::move 的插入是有效的。
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.第一行将输出 0,因为复制构造无效。 The second line will output 1 since the move construction is valid.第二行输出1,因为移动结构是有效的。
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...编辑:谜团还在继续,vc 为测试返回了不正确的 11 ......
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:然而,这很麻烦,我建议像这样定义 addEntity:
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(...));
Not sure if this solution could help you as well, but I suddenly got the same error on a private std::map<int, std::unique_ptr<SomeType>>
data member when I switched from a static library to a dynamic library in Visual Studio 2015 (Update 2)
.不确定这个解决方案是否也能帮助你,但是当我从静态库切换到动态库时,我突然在私有std::map<int, std::unique_ptr<SomeType>>
数据成员上遇到了同样的错误Visual Studio 2015 (Update 2)
。
Since using template data members together with __declspec(dllexport)
produces a warning (at least in MSVC), I resolved that warning by (almost) applying the PIMPL
(Private Implementation) idiom.由于将模板数据成员与__declspec(dllexport)
一起使用会产生警告(至少在 MSVC 中),因此我通过(几乎)应用PIMPL
(私有实现)习语解决了该警告。 Surprisingly, the C2280 error also disappeared that way.令人惊讶的是,C2280 错误也以这种方式消失了。
In your case, it would be:在您的情况下,它将是:
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();
// ...
};
and in the .cpp file:并在 .cpp 文件中:
EntityManager::EntityManager() :
d( new EntityManagerPrivate() )
{
}
EntityManager::~EntityManager()
{
delete d;
d = nullptr;
}
// in all other methods, access map by d->map
Note that for a real PIMPL
you would have to move the private class into an own header file which is only referenced by the .cpp.请注意,对于真正的PIMPL
您必须将私有类移动到仅由 .cpp 引用的自己的头文件中。 The actual header would only have a forward declaration class EntityManagerPrivate;
实际的标头只有一个前向声明class EntityManagerPrivate;
after the includes.在包含之后。 For a real PIMPL
, the private class would also have to have implementation in addition to data members.对于真正的PIMPL
,私有类除了数据成员之外还必须有实现。
I had the same issue on VS 2017 with msvc 14.15.26726.我在使用 msvc 14.15.26726 的 VS 2017 上遇到了同样的问题。 According to the compiler error log, things seem to be related to the need for a copy ctor for std::pair<_kT, _T> during instantiation.根据编译器错误日志,事情似乎与在实例化期间需要 std::pair<_kT, _T> 的复制构造函数有关。 I don't know why, but one interesting observation (and workaround) for me is to put a declaration of a std::unique_ptr before the declaration of the map, eg:我不知道为什么,但对我来说一个有趣的观察(和解决方法)是在地图声明之前放置一个 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.