问题 map move-insertion是否保证元素是否被移动?


C ++中的标准“map”容器允许您插入rvalue:

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)));

但这是否保证不动?


8579
2017-12-02 12:21


起源

你可以永远 find() 先... :) - Nim
@Nim:一般来说效率不高。如果有什么, lower_bound 带有暗示插入;我对此很清楚。 - Kerrek SB
顺便说一下,调用返回值可能会产生误导 it当它是一对。我猜这个问题相当于“是 insert 允许建造一个 value_type 在它不进行插入的情况下从其参数中获取对象?“。我认为答案应该是”否“,因为模板版本等同于”emplace“,并且对于emplace来说它将是相当差的除非必要,否则构造一个值 - 整个安置点就是避免构造一个 value_type 参数。但是,这实际上是否有保障是另一回事,所以我担心这是我得到的:-) - Steve Jessop
尝试 m.emplace_back(1, std::move(x));。 - Xeo
@Xeo:一直试图找到支持它的编译器:-)你有什么保证吗? - Kerrek SB


答案:


虽然 std::move 实际上并没有执行任何动作,也没有 std::make_pairstd::make_pair 将其论点转发给 std::pair 构造函数,从这些参数初始化其两个值成员。

因此,移动是在此之前执行的 std::map 有机会做任何事情。所以,是的,你没有充分的理由最终得到了“破碎”的举动。

你应该可以利用 emplace (为了跳过这对结构)。从表102:

效果:插入一个 T 目的 t 用。构造 std::forward<Args>(args)... 当且仅当容器中没有元素,其键与等效键相同 t

显然,图书馆此时仍在“转发”,因此它是预先移动的,在你的情况下,不会发生任何安置,所以整个表达应该是一个有效的无操作。

然而的libstdc ++ 从 GCC 4.8.0似乎有一个bug 在这方面: 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

可以说这个标准在这方面是模棱两可的,但我称之为违背意图。然后, clang v3.4 with 的libc ++ 也表现出这种行为和Visual Studio 2012一样。如果我的标准解释是正确的,那么所有三个主流工具链都会失败。

我猜他们都决定将“if if only only”应用于插入,而不是插入  那个工程。

我已经发布了一个问题 STD-讨论 旨在激发对表102的通过的改进,以便一劳永逸地权威地回答这个问题。


16
2017-12-02 12:43



老实说,我认为 注意 在标准中指出这一点在这里是明智的,但我认为我们现在可以推断出上述情况。 - Lightness Races in Orbit
太大了,谢谢!我发现那些“容器需求表”是标准中最难扫描的部分之一...... - Kerrek SB
嗯......你真的能用这个独特的指针做这项工作吗?你怎么拼插 - m.emplace(1, std::move(ptr))?我无法在GCC 4.8.2中工作,即指针仍然为空。 - Kerrek SB
@KerrekSB:是的,似乎确实:((coliru.stacked-crooked.com/a/e6721d6afd140c18)编译器错误? - Lightness Races in Orbit
看来标准文字似乎如此?!关联安置支持在即将到来时非常缓慢(并且看到我的编辑:GCC 4.6和4.8之间的对参考代码也存在差异),所以这完全有可能是错误的。 - Kerrek SB