我正在循环生成std::unique_ptr<Device>
,并将它们添加到成员变量std::map<size_t, std::unique_ptr<Device>>
中。
标题:
#include "util.h"
#include <map>
#include <memory>
class Container {
public:
explicit Container(char* path);
private:
std::map<size_t, std::unique_ptr<Device>> devices_;
}
实施:
Container::Container(char* path) {
std::vector<char*> files = util::list_files(path);
for(size_t i = 0; i < files.size(); i++) {
auto device = util::CreateDevice(file); // Returns std::unique_ptr<Device>
devices_.insert({i, std::move(device)});
}
}
util.h
负责创建unique_ptr<Device>
,但是我没有源代码,因此无法更改它。插入地图时,我需要调用std::move
,否则它会在超出范围时立即销毁(例如,下一次迭代)。
此实现可以很好地构建,并且可以在Visual Studio 2017编译器上工作。但是,在使用叮当声的Travis-CI上,出现以下错误:
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.5.0/../../../../include/c++/5.5.0/ext/new_allocator.h:120:23: error:
call to implicitly-deleted copy constructor of 'std::pair<const unsigned
long, std::unique_ptr<Device
std::default_delete<Device> > >'
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
似乎我的实现在创建要插入到地图中的对时试图复制unique_ptr
。如何避免这种情况?
答案 0 :(得分:2)
您可以就地构建它,避免创建任何临时对象:
devices_.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(i)
, ::std::forward_as_tuple(::std::move(device))
);
答案 1 :(得分:1)
如果您以后可以插入重复的键覆盖先前的键,那么您可以:
devices_[i] = std::move(device);
如果没有,那么:
devices_.insert(std::make_pair(i, std::move(device)));
后者起作用且问题中的代码不起作用的原因是,在C ++ 17之前,insert
方法具有一组有缺陷的重载,您可以看到here :
std::pair<iterator, bool> insert( const value_type& value ); // 1
template<class P>
std::pair<iterator,bool> insert( P&& value ); // 2
通常,重载#2应该处理需要从中移出参数的情况,但是在您的情况下,由于您选择了传递括号初始化列表,因此无法推论P
,并且#1赢得重载解析并尝试复制该参数。通过使用std::make_pair
,您可以强制推论对#2成功,然后就可以获胜并做正确的事情。
在C ++ 17中,此问题已解决,因为添加了#3重载:
std::pair<iterator,bool> insert( value_type&& value );
这将通过braced-init-list参数赢得重载解析。