【问题标题】:How to efficiently move (some) items from one std::map to another?如何有效地将(某些)项目从一个 std::map 移动到另一个?
【发布时间】:2020-03-09 09:03:54
【问题描述】:

我有两个std::map<> 对象ab,并且想根据一些谓词p 将一些元素(节点)从一个映射移动(extract + insert)到另一个映射。

for (auto i = a.begin(); i != a.end(); ++i)
    if (p(*i))
        b.insert(a.extract(i))

此代码在 clang 中出现段错误。我认为问题是i 在其节点从 a 中提取后的增量。

是通过使用后增量来解决此问题的正确/唯一方法吗?例如:

for (auto i = a.begin(); i != a.end();)
    if (p(*i))
        b.insert(a.extract(i++))
    else
        ++i;

编辑:我删除了关于“为什么这在 gcc 中有效?”的部分,因为我无法在我当前的设置中重现它。我确信它曾经在某个时间点使用过,但是使用 gcc 9.2.1 我遇到了死锁(而不是段错误)。无论哪种方式,在extract() 之后递增都不起作用。

【问题讨论】:

  • @Eljay 在我看来,C++17 中的新映射“node handle”拼接 API 已经足够专业化,足以证明自己的问题。我希望这不会因为重复而关闭。
  • Deleting elements from std::set while iterating 的可能重复项。 std::setstd::map 非常相似,据我所知,extracterase 具有相同的失效含义。
  • 你用的是什么版本的clang和gcc?对我来说,使用 clang 8.0 和 gcc 7.4 都会导致段错误。
  • 我很惊讶这段代码可以在任何编译器中运行。您没有处理由 extract 引起的失效

标签: c++ stl c++17 stdmap


【解决方案1】:

我认为问题是从 a 中提取节点后 i 的增量。

的确如此。提取使提取元素的迭代器无效,i 就是这样的迭代器。通过无效迭代器递增或间接的行为是未定义的。

为什么这似乎在 gcc 中有效,但在 clang 中无效?

因为程序的行为是未定义的。

是通过后增量解决此问题的正确/唯一方法吗?

这是解决此问题的正确方法。这不是一个特别糟糕的方式。如果您不想重复增量,一种方法是使用变量:

for (auto i = a.begin(); i != a.end();) {
    auto current = i++;
    if (p(*current)) {
        // moving is probably unnecessary
        b.insert(a.extract(std::move(current)));
    }
}

【讨论】:

  • 这是一个好方法,(合理地)假设复制迭代器的状态比复制节点更便宜。
  • @Spencer 复制迭代器通常很简单。但我添加了一个动作以防万一。
  • @Spencer current 将保持从状态移动。无论该状态是什么都无关紧要,因为此后不再使用它。
  • @eeroika 谢谢,我更仔细地查看了您的代码并意识到这一点。
  • 我更喜欢你的局部变量而不是有两个 icrements,但我建议做一个小的改进:通过使用 c++17s if (auto current = ++i; p(*current)) 语法来限制 current 的范围。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-04
相关资源
最近更新 更多