C ++在不复制的情况下在std :: unique_ptr上调用std :: move

时间:2018-11-07 20:27:14

标签: c++ unique-ptr

我正在循环生成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。如何避免这种情况?

2 个答案:

答案 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参数赢得重载解析。