查看以下代码:
#include <utility>
#include <map>
// non-copyable but movable
struct non_copyable {
non_copyable() = default;
non_copyable(non_copyable&&) = default;
non_copyable& operator=(non_copyable&&) = default;
// you shall not copy
non_copyable(const non_copyable&) = delete;
non_copyable& operator=(const non_copyable&) = delete;
};
int main() {
std::map<int, non_copyable> map;
//map.insert({ 1, non_copyable() }); < FAILS
map.insert(std::make_pair(1, non_copyable()));
// ^ same and works
}
在取消注释g ++ 4.7上的标记行时,编译此代码段失败。产生的错误表明non_copyable
无法复制,但我希望它能被移动。
为什么插入使用统一初始化构造的std::pair
会失败,而不是使用std::make_pair
构建的{{1}}?两者都不应该产生可以成功移入地图的右值吗?
答案 0 :(得分:18)
[这是完全重写。我之前的回答与问题无关。]
map
有两个相关的insert
重载:
insert(const value_type& value)
和
<template typename P> insert(P&& value)
。
使用简单列表初始化程序map.insert({1, non_copyable()});
时,会考虑所有可能的重载。但是只找到了第一个(const value_type&
),因为另一个没有意义(没有办法神奇地猜测你是想创造一对)。第一个重载当然不起作用,因为你的元素不可复制。
您可以通过显式创建对,使用make_pair
,如您所述,或通过明确命名值类型来使第二个重载工作:
typedef std::map<int, non_copyable> map_type;
map_type m;
m.insert(map_type::value_type({1, non_copyable()}));
现在list-initializer知道查找map_type::value_type
构造函数,找到相关的可移动构造函数,结果是一个rvalue对,它绑定P&&
- insert
的重载功能
(另一种选择是将emplace()
与piecewise_construct
和forward_as_tuple
一起使用,但这会更加冗长。)
我认为这里的道德是list-initializers寻找可行的重载 - 但是他们必须知道要寻找什么!
答案 1 :(得分:0)
除了提供移动(赋值)构造函数的其他答案之外,您还可以通过指针(尤其是unique_ptr
)存储不可复制的对象。 unique_ptr
将为您处理资源转移。