11

如果我将一对移动到地图中,但由于密钥已经存在而导致插入失败,那么之后我可以安全地使用这对吗?

//objects available: map, pair

auto insert_pair = map.insert(std::move(pair));

if (!insert_pair.second)
{
  //can I safely access pair here?
}

这是否已记录在标准中?

4

4 回答 4

7

对于这看起来多么无意义(阅读下文),鉴于规范的当前状态,您无法对函数调用返回后参数的状态做出任何假设。

要了解原因,让我们首先指出insert()成员函数是根据emplace()(参见 23.4.4.4/1)定义的:

第一种形式等价于return emplace(std::forward<P>(x))。[...]

的后置条件emplace()依次指定为(参见第 23.2.4 节,表 102):

value_typetstd::forward<Args>(args)... 当且仅当容器中没有与 的键等效的元素时,才插入由构造 的 对象t。返回的对的bool组件是true当且仅当插入发生时,并且对的迭代器组件指向具有与 的键等效的键的元素t

上面引用的粗体句子(重点是我的)表示,如果键不存在,将成为映射元素的对将直接从您提供的右值对移动构造。

这将非常非常合理地推断出实现首先必须检查键是否存在,并且只有在不存在的情况下,才从您的对中移动构造新地图的元素。

然而,在处理形式语言的规范时,“非常非常合理”不是一个有效的论据。在这种情况下,从形式上看,没有什么可以阻止实现执行以下操作:

  1. 首先从你的参数中移动构造一对tmp(这意味着你不能在函数返回后对参数的状态做出假设);
  2. 检查密钥是否已经存在于地图中;
  3. 如果没有,请进行必要的内务处理以插入tmp容器。

甚至:

  1. 检查密钥是否存在于地图中;
  2. 如果是这样,请插入一个从您的论点移动构造的新元素;
  3. 如果不是,请从您的论点中移动构造一个新对象,并且不对其进行任何操作。

上面的第 3 点是绝对没有意义的,但它并没有被正式禁止。注意措辞:

当且仅当容器中没有与 的键等效的元素时,才插入由构造 的value_type 对象。tstd::forward<Args>(args)...t

这只是说如果容器中没有与 key 等效的元素t,则不会将移动构造的对象t插入到地图中 - 但是,这听起来多么愚蠢,它并没有说在all应从 : 移动构造t:只要它不插入到地图中,这是允许的。

这就是说,由于标准没有明确限制这方面的实现,因此您无法假设您的论点是否被移出。因此,您不能对函数调用返回时您的配对将处于的状态做出假设(根据第 17.6.5.15 段)。

但是,如果我可以让个人意见潜入,我相信这是一个缺陷。

于 2013-07-09T16:13:16.943 回答
3

我找不到任何具体的东西,但就标准而言,关于 STL 定义的类型(例如,对):

17.6.5.15:除非另有规定,否则此类移出的对象应置于有效但未指定的状态。

如果不满足“否则指定”,我无法找到“失败”的 map::insert,这意味着按照标准你不能使用这对。实际上,基于有意义的实现,我想这对将保持不变,但依赖于此将落入未定义的编译器/特定于 stl 的行为的领域。

于 2013-07-09T15:47:47.230 回答
2

从c++11 标准(草案 n3337)第23.2.4节中的表 102中陈述了以下内容(适用于这种情况的表达式):a_uniq.insert(t)

要求:如果 t 是非 const 右值表达式,value_type 应为 MoveInsertable into X;否则,value_type 应为 CopyInsertable 到 X 中。 效果:当且仅当容器中不存在具有与 t 的键等效的键的元素时插入 t。当且仅当插入发生时,返回的对的 bool 组件为真,并且对的迭代器组件指向具有与 t 的键等效的键的元素。

它没有说明插入是否不发生的任何影响t(我不确定这是否属于未指定实现定义的行为)。我找不到任何其他提供进一步说明的条款,因此该标准似乎没有提供明确的答案。可以将发布的代码重写为仅insert()在该对不存在或仅使用返回的迭代器时才调用。

于 2013-07-09T16:08:26.427 回答
-2

是的,你可以使用这对。Insert 将返回对映射中已存在的对的引用。

文档在这里: http ://www.cplusplus.com/reference/map/map/insert/

于 2013-07-09T15:32:59.963 回答