2

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.

buj
  • 165
  • 5
  • 1
    Typically, if you find something is too much code, you can fix the problem by making it a function. Suddenly, the task that is takes too many lines to write can be done in a single line. – François Andrieux Jan 15 '20 at 18:08
  • Yes. But I believe that this is a pretty common task, so I would assume C++ commitee (or whoever designs the language) have a solution to this. – buj Jan 15 '20 at 18:09
  • 2
    @buj: "*I believe that this is a pretty common task*" That's the problem. C++, as a language and as a standard library, generally does not like it when a type is not default constructible. It is possible to make and use such types, but broadly speaking, your ability to use such types in generic C++ code will be limited. – Nicol Bolas Jan 15 '20 at 18:22

1 Answers1

5

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, assigns std::forward<M>(obj) to the mapped_type corresponding to the key k. If the key does not exist, inserts the new value as if by insert, constructing it from value_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));

full program demo

YSC
  • 38,212
  • 9
  • 96
  • 149