Reputation: 978
I have a std::map<Key, T>
with a non-default-constructible T
. T
operloads operator +
so that I know how to add objects of T
. I am frequently in the situation that I need to add a particular value at at a given Key k
. If T
were default constructible I would do something like
std::map<Key, T> map;
Key k;
T t;
map[k] += t;
However I am forced to use expressions like
if (map.contains(k)) map.at(k) += t;
else map.emplace(k, t);
Is there a way of avoiding the lookup and behave like []
would do with a default value? Do I need to change the allocator of map
so that by default inserts t
?
Upvotes: 1
Views: 246
Reputation: 503963
What you want, in general, is try_emplace
. This is in C++17.
This function will emplace if and only if the key is missing, otherwise it is guaranteed to do nothing to the value. This is unlike insert
or emplace
, which may freely copy or move your value even if the key exists. This is important if your type is like unique_ptr
, or to avoid unnecessary copies.
#include <iostream>
#include <map>
#include <string>
struct Foo {
explicit Foo(int i) : i(i) {}
Foo(const Foo&) = delete;
Foo(Foo&&) = default;
Foo& operator+=(const Foo& other) {
i += other.i;
return *this;
}
int i;
};
template <typename K, typename V>
V& emplace_or_add(std::map<K, V> & map, const K& key, V&& value) {
auto [iter, was_emplaced] = map.try_emplace(key, std::forward<V>(value));
if (!was_emplaced) {
iter->second += value;
}
return iter->second;
}
int main() {
std::map<char, Foo> map;
map.emplace('a', 5);
emplace_or_add(map, 'a', Foo(3));
emplace_or_add(map, 'b', Foo(2));
std::cout << "a: " << map.at('a').i << std::endl;
std::cout << "b: " << map.at('b').i << std::endl;
}
http://coliru.stacked-crooked.com/a/2b739a55ad791507
a: 8
b: 2
Upvotes: 2
Reputation: 217448
To look-up only once, you might do something like:
if (auto [it, inserted] = map.insert(k, t); !inserted) {
*it += t;
}
Upvotes: 3