c++stdstdmapdeleted-functions

std::map: Creating/replacing an element when it is not default-constructible


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.


Solution

  • 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