映射移动插入是否保证元素被移动或不被移动
Does map move-insertion guarantee that elements are or are not moved from?
C++中的标准"map"容器允许您插入右值:
T x;
std::map<int, T> m;
// m[1]; // populate "1"
auto it = m.insert(std::make_pair(1, std::move(x)));
问题是当元素已经存在时会发生什么,即 it->second == false
.元素是否已x
"移出"?例如,如果它是一个唯一的指针,x
会被重置为 null 吗?
显然,上述情况下的答案是"是",因为移动在创建对时已经发生。但是假设现在我想更新现有值,但仍保留该值是否已存在的信息(所以我不能只说m[1] = std::move(x);
(。在这种情况下,是否可以"不离开"对象?
我在 GCC 中发现以下工作 [更新:适用于 GCC 4.6,不适用于 GCC 4.8]:
auto it = m.insert(std::pair<const int, T &&>(1, std::move(x)));
但这能保证不动吗?
虽然std::move
实际上并没有执行任何移动,std::make_pair
也没有,但std::make_pair
将其参数转发给std::pair
构造函数,该构造函数从这些参数初始化其两个值成员。
因此,移动是在std::map
有机会执行任何操作之前执行的。所以,是的,你最终会无缘无故地"破碎"的举动。
您应该能够利用emplace
(为了跳过对结构(。从表 102:
效果:插入一个
T
对象t
当且仅当容器中没有元素的键等同于t
键时,使用std::forward<Args>(args)...
构造。
显然,此时库仍在"转发",因此它是预移动的,在您的情况下,不会发生任何放置,因此整个表达式应该是有效的无操作。
但是,来自GCC 4.8.0的libstdc++似乎在这方面存在一个错误:emplace
调用内部树上的_M_emplace_unique
,将参数转发给_M_create_node
,将参数转发给allocator_traits<_Node_allocator>::construct
,将参数转发给_S_construct
,将参数转发给__a.construct
,使用默认分配器,std::allocator<std::pair<const _Key, _Tp> >::construct
, 这是您试图避免的对构造函数...全部在碰撞检查_M_emplace_unique
之前。
可以说该标准在这方面是模棱两可的,但我称之为违反意图。再一次,带有libc ++的clang v3.4也表现出这种行为,Visual Studio 2012也是如此。因此,如果我的标准解释是正确的,那么这在所有三个主流工具链上都失败了。
我猜他们都认为"当且仅当"适用于插入,而不是插入和构造。
我已经发布了一个关于性病讨论的问题,旨在激发对表 102 段落的改进,以便一劳永逸地权威地回答这个问题。
- 在同一容器中移动元素
- 从标准容器中移动元素是否合法?
- 我可以在 std::list 中移动元素而不会使迭代器或引用无效,但是如何移动呢?
- 在数组中移动元素 SIGABRT 错误
- C++11 在列表到映射(或其他容器)之间移动元素
- 在堆栈中移动元素
- 初始化 std::数组而不复制/移动元素
- 算法,用于查找获取排序列表所需的最小移动元素到结束步骤
- 从Boost Multi_index数组移动元素
- 将一个向量附加到另一个向量时,为什么移动元素比复制它们便宜?
- memmove会移动元素(就像前面的方式相同),还是一次抓住整个内存块
- 在C++数组中移动元素
- 为什么重新分配矢量复制而不是移动元素
- 在矢量中插入元素并在其后移动元素
- 弹出后使用数组>>移动元素排队
- 从STL容器中移动元素是否会将其从该容器中移除
- 对容器进行排序,然后在保留原始排序的情况下移动元素
- 可移动元素向量的大小调整是否有效?
- 我如何构建公共数据,同时保留默认的移动元素和分配
- 扩展std::vector以从其他vector类型移动元素