问题 将其移动到地图后访问一对


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

//objects available: map, pair

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

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

这是否已在标准中记录?


10500
2017-07-09 15:27


起源

如果标准中确实没有明确规定,您可以使用解决方法 map::find 确定密钥是否存在,以及 move 只有它没有 - Praetorian
@Praetorian,可能值得使用 map::lower_bound 要么 map::upper_bound 然后使用提示插入以避免增加复杂性。 - Lol4t0


答案:


对于看起来如何无意义(如下所示),给定规范的当前状态,您不能在函数调用返回后对参数的状态做出任何假设。

要明白为什么,让我们先指出一下 insert() 成员函数是根据定义的 emplace() (见23.4.4.4/1):

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

后期条件 emplace() 反过来又指定为(参见第23.2.4节,表102):

插入一个 value_type   目的 t 用。构造    std::forward<Args>(args)...   当且仅当没有   容器中的元素   键相当于键 t。   该 bool 的组成部分   返回的对是 true 如果和   只有插入需要   地方和迭代器   该对的组成部分   用键来元素   相当于的关键 t

从上面引用的粗体句子(强调是我的)说如果键不存在将成为地图元素的那对将被移动构造  从您提供的右值对。

这样就可以了 非常合理 推断出实现首先必须检查密钥是否存在,并且只有在不存在的情况下,从您的对中移动构造新映射的元素。

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

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

甚至:

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

上面的第3点绝对毫无意义,但并未正式禁止。记住措辞:

插入一个 value_type   目的 t 用。构造    std::forward<Args>(args)...   当且仅当没有   容器中的元素   键相当于键 t

这只表示如果容器中没有元素,其键等效于键 t,没有移动构造的对象 t 将被插入到地图中 - 但是,对于这听起来多么愚蠢,它并没有这么说 完全没有任何对象 应由..​​.构成 t:只要它没有插入到地图中,就允许这样做。

这就是说,既然标准没有明确约束这方面的实现,你就不能假设你的论证是否被移除了。因此,当函数调用返回时,您不能对您的对将处于的状态进行假设(根据第17.6.5.15段)。

但是,如果我可以透露个人意见,我相信这是一个缺陷。


7
2017-07-09 16:13



伟大的A,也许你可以把它移到ISO lvl并澄清...... - NoSenseEtAl
@NoSenseEtAl:谢谢。什么是ISO lvl? - Andy Prowl
我的意思是向ISO委员会提出澄清请求(也请将其放在标准中)。对于我认为以这种或那种方式澄清它的实施者和用户将是有帮助的。我不是说提出建议或者做什么,也许只是在isocpp论坛上发帖就足以让iso ppl注意到了。 :) - NoSenseEtAl
@NoSenseEtAl:是的,也许这不是一个坏主意。我会在std讨论中问 - Andy Prowl
请告诉我们他们说的话! - roger.james


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

17.6.5.15:除非另有规定,否则此类移动物体              应置于有效但未指明的状态。

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


3
2017-07-09 15:47





表102 在部分 23.2.4关联容器 在c ++ 11标准(草案n3337)中,陈述了以下内容(表达式) a_uniq.insert(t) 在这种情况下适用):

要求:如果t是非const rvalue表达式,则value_type应为MoveInsertable为X;否则,value_type应为CopyInsertable到X.效果:当且仅当容器中没有元素且密钥等于t的键时才插入t。当且仅当插入发生时,返回对的bool组件才为真,并且该对的迭代器组件指向具有等效于t键的键的元素。

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


2
2017-07-09 16:08





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

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


-2
2017-07-09 15:32



我想你在这里谈论错误的一对。问题是原始对在尝试后是否可用但未能移动。 - zennehoy