【问题标题】:Accessing a pair after moving it into a map将一对移动到地图后访问它
【发布时间】:2013-07-07 07:03:02
【问题描述】:

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

//objects available: map, pair

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

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

标准中是否记录了这一点?

【问题讨论】:

  • 如果标准中确实没有明确规定,作为一种变通方法,您可以使用map::find 来确定密钥是否存在,并在不存在时使用move 它跨度>
  • @Praetorian,可能值得使用map::lower_boundmap::upper_bound,然后使用带有提示的插入来避免增加复杂性。

标签: c++ c++11 map move-semantics std-pair


【解决方案1】:

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

要了解原因,我们首先指出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)... 当且仅当没有 容器中的元素 key 等同于t 的密钥。 的bool 组件 返回的对是 true 如果并且 仅当插入需要 位置和迭代器 对点的分量 到带有键的元素 相当于t的key。

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

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

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

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

甚至:

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

以上第 3 点绝对没有意义,但并未正式禁止。注意措辞:

插入value_type 对象t 构造与 std::forward<Args>(args)... 当且仅当没有 容器中的元素 相当于t的key。

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

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

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

【讨论】:

  • @NoSenseEtAl:谢谢。什么是 ISO 等级?
  • 我的意思是向 ISO 委员会提出澄清请求(也就是请把它放在一个标准中)。我认为以一种或另一种方式澄清它对实施者和用户会有所帮助。我不是说提出建议或什么,也许只是在isocpp论坛上发帖就足以让iso ppl注意到。 :)
  • @NoSenseEtAl:是的,也许这不是一个坏主意。我会在标准讨论中提问
  • 请让我们了解他们所说的内容!
【解决方案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()

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 我认为您在这里谈论的是错误的配对。问题是原始对在尝试但未能移动后是否可用。
      猜你喜欢
      • 2015-06-02
      • 2014-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-23
      • 2018-01-06
      • 1970-01-01
      相关资源
      最近更新 更多