映射移动插入是否保证元素被移动或不被移动

Does map move-insertion guarantee that elements are or are not moved from?

本文关键字:移动 元素 插入 是否 映射      更新时间:2023-10-16

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 段落的改进,以便一劳永逸地权威地回答这个问题。