Suppose you have a class which is not default constructible.
class A {
private:
int a;
public:
A() = delete;
A(int a0) : a(a0) {}
};
Now, we have some map Int --> A, std::map<int, A> mapping
. Let's say we want to create a new mapping for some key 0, and if the key exists, we want to replace the old value. The way to do this for default-constructible classes is this:
mapping[0] = A(4);
This will however fail for class A, because operator[] first constructs a default instance of A, and only then will it assign the value of A(4)
. One way to do this in general (i.e. for non-default-constructible classes) is this:
auto it = mapping.find(0);
if (it == mapping.end()) {
mapping.insert(0, A(4));
}
else {
it->second = A(4);
}
My question is: is this really the (C++) intended way to do this? I feel this cannot be right; as a programmer I do not want to write so much code for so little. But there seems to be no simple way out: I have looked up common map methods (insert, emplace, emplace_hint) and all of them do nothing if the key is already present.
I have looked up common map methods (insert, emplace, emplace_hint) and all of them do nothing if the key is already present.
C++17 have addressed that point with std::map::insert_or_assign
:
template <class M> pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);
If a key equivalent to
k
already exists in the container, assignsstd::forward<M>(obj)
to themapped_type
corresponding to the keyk
. If the key does not exist, inserts the new value as if by insert, constructing it fromvalue_type(k, std::forward<M>(obj)
).
This makes
auto it = mapping.find(0);
if (it == mapping.end()) {
mapping.insert(0, A(4));
}
else {
it->second = A(4);
}
looks like
mapping.insert_or_assign(0, A(4));