如何高效且习惯地在unordered_map
中设置值并找出是否添加了新键:
#include <unordered_map>
#include <string>
int main() {
auto map = std::unordered_map<std::string, int>{{"foo", 1}, {"bar", 2}};
map["foo"] = 3;
// how to find out if a new key was added?
}
我不能直接使用insert()
,因为如果已经有一个值,我想覆盖该值,而insert
不会这样做。我不能直接使用operator[]
,因为它没有提供有关是否添加了新密钥的信息。
出于性能原因,我想避免在地图中进行两次搜索。
我在其他地方看到的一个技巧是获取引用并检查该值是否是默认构造的:
auto& value = map["foo"];
if(value == 0) {
// am inserting a new key
}
value = 3;
但是我可以在我的地图中真正拥有默认构造的值,因此默认构造值不能很好地指示新键。
到目前为止,我能想到的最好的是:
auto size_before = map.size();
map["foo"] = 3;
if (map.size() > size_before) {
// am inserting a new key
}
这看起来很丑陋,它假设获得unordered_map
的大小很便宜(是吗?
看起来unordered_map::insert_or_assign
可能是我祈祷的答案,但遗憾的是它出现在 C++17 年,所以我可能再过 5 年左右才能使用它。想要这样做似乎是一件很常见的事情,所以我认为目前必须有一种合理的方法来做到这一点。
std::unordered_map::insert
和测试结果。
带辅助功能:
template<typename Map, typename T>
std::pair<typename Map::iterator, bool>
insert_or_assign(Map& m, const typename Map::key_type& k, const T& t)
{
auto p = m.insert({k, t});
if (!p.second) {
// overwrite previous value
p.first->second = t;
}
return p;
}
然后
auto p = insert_or_assign(map, "foo", 3);
if (p.second) {
// inserted
} else {
// assigned
}
现场演示
insert()
成员函数,该函数将返回一个pair
,该函数返回一对,该函数由插入元素(或阻止插入的元素(的迭代器和指示插入是否发生的布尔值组成。 它看起来像
if (map_name.insert(some_value).second)
value was inserted
else
value was not inserted
如果需要在未插入元素时修改映射中的值,则可以使用
auto ret = map_name.insert(some_value);
if (!ret.second)
*(ret.first) = some_value;
编辑:更新
从 C++17 开始,std::map
和std::unordered_map
成员函数insert_or_assign()
。 如果键/值对不在映射中,此函数将插入到容器中,如果键已存在,则此函数将覆盖容器中的现有值。 该函数将返回一个std::pair
,其中包含插入/更新元素的迭代器和布尔信号,指示是否有插入。 如果有插入,那么它将true
否则false
std::unordered_map<std::string, int> foo = { { "foo", 1 },{ "bar", 2 } };
auto ret = foo.insert_or_assign("foo", 3);
if (ret.second)
std::cout << "foo was inserted";
else
std::cout << "foo already exist. new value: " << ret.first->second;
输出:
foo already exist. new value: 3
我目前找不到支持 C++17 的在线编译器,我可以共享代码,但您可以在此处或 Visual Studio 2015 Microsoft运行它