C++ 在 std::unique_ptr 上调用 std::move 而不复制
C++ calling std::move on std::unique_ptr without copying
我在一个循环中生成一个 std::unique_ptr<Device>
并将它们添加到一个 std::map<size_t, std::unique_ptr<Device>>
这是一个成员变量。
Header:
#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 上使用 clang,我收到以下错误:
/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
。我怎样才能避免这种情况?
您可以就地构建它,避免创建任何临时对象:
devices_.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(i)
, ::std::forward_as_tuple(::std::move(device))
);
如果您可以接受后续插入的重复键覆盖之前的键,那么:
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 参数赢得重载决议。
我在一个循环中生成一个 std::unique_ptr<Device>
并将它们添加到一个 std::map<size_t, std::unique_ptr<Device>>
这是一个成员变量。
Header:
#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 上使用 clang,我收到以下错误:
/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
。我怎样才能避免这种情况?
您可以就地构建它,避免创建任何临时对象:
devices_.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(i)
, ::std::forward_as_tuple(::std::move(device))
);
如果您可以接受后续插入的重复键覆盖之前的键,那么:
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 参数赢得重载决议。