如何将一个std::map中的所有键值对移动到另一个std::map中。

4
假设我有以下内容:
std::map<KEY,VALUE> m1;
std::map<KEY,VALUE> m2;

什么是将所有键/值对从m1移动到m2的最直接方法?
我期望:
- 此操作后,m1为空 - m2可能最初有一些键值对 - 那些在m2中没有与m1相同键的键值对应该保持原样 - 那些在m2中与m1有相同键的键值对应该被m1的键值对覆盖
我需要从调用一系列组合吗?
解决方案:
James Kranze的解决方案满足我的要求。
for( const auto& p : m1 )
  m2[ p.first ] = p.second;
m1.clear();

Joachim Pileborg的建议仅在m2和m1没有相同的键(即对于相同的键,m2的值不会被m1的值覆盖)时才起作用。

std::move( m1.begin(), m1.end(), std::inserter( m2, m2.begin() ));

元素的复制是允许的吗?还是你字面上的意思是语言中的“移动”? - Lightness Races in Orbit
1
就像这种情况,你会真正欣赏到list::splice。当你思考时,关联容器没有交换节点的接口是相当令人惊讶的。 - Matthieu M.
2个回答

5
最明显的解决方案就是自己编写一个循环:
for ( std::map<KEY, VALUE>::const_iterator current = m1.begin();
        current != m1.end();
        ++ current ) {
    m2[current->first] = current->second;
}

否则,我认为以下类似的内容应该可以工作:
std::copy( m2.begin(), m2.end(), std::inserter( m1, m1.end() ) );
m2.clear();
m2.swap( m1 );

这不是很直观,我有疑虑在没有注释的情况下使用它,因为:

  1. 由于 std::map 没有 push_backpush_front,您需要使用更通用的 inserter,进而需要指定插入位置的迭代器。但是 std::map 将此迭代器视为“提示”,由于它通常不是一个好的提示,因此它将被忽略。

  2. 您实际上需要从 m2 复制到 m1,因为插入到映射中不会覆盖任何现有值,当键存在于两个映射中时,您想要保留来自 m1 的值。


+1 这看起来不错 - 你的第二点是关键,这就是为什么 std::move() 单独使用对我的要求不足的原因 - 谢谢。 - kfmfe04
在这种情况下,m2[std::move(current->first)] = std::move(current->second);能否避免一些复制?(例如,如果KEY和VALUE是std :: string) - Arzar
@ThomasPetit:你可以移动 second,但不能移动 first,因为映射键是不可变的。 - Mike Seymour
@ThomasPetit 在表达式中添加 move 这样的简单操作,比如 m2[current->first] = current->second;,是过早的优化。在性能分析工具告诉你必须这么做之前,保持简单(并且在这种情况下,也要保持可移植性)。 - James Kanze

5

1
std::move( m1.begin(), m1.end(), std::inserter( m2, m2.begin()) 看起来可以解决问题 - 但不确定在 m1 和 m2 中都存在的键。需要进行验证。 - kfmfe04
看起来如果一个键存在于m1和m2中,m2的键值对将不会被m1中的键值对覆盖... - kfmfe04
1
这里实际上可以使用 std::move 吗?我没有使用过它,但如果我理解正确的话,它会导致源元素的修改。如果这些元素在 std::map 中,那么就会导致未定义的行为。 - James Kanze
@JamesKanze 没有UB(未定义行为)。关键部分由于是const限定符而被复制。使用inserter将不会覆盖。 - T.C.
假设建议是 m2 = std::move(m1);,这将销毁 m2 的任何先前内容,而不是合并来自 m1 的值。原帖作者想要 m2 保留任何未被 m1 覆盖的原始内容。 - Mark Lakata

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接