How to emplace object with no-argument constructor into std::map?

C++C++11

C++ Problem Overview


I want to emplace an object into a std::map whose constructor does not take any arguments. However, std::map::emplace seems to require at least one additional argument besides the key. So how can I forward zero arguments to the constructor?

C++ Solutions


Solution 1 - C++

The element type of std::map<K, V> is actually std::pair<K, V>, so when you are emplacing into a map, the arguments will be forwarded to the constructor of std::pair. That's why you can't pass just the key: std::pair<K, V> can't be constructed from a single argument (unless it's another pair of the same type.) You can pass zero arguments, but then the key will be value-initialized, which is probably not what you want.

In most cases, moving values will be cheap (and keys will be small and copyable) and you should really just do something like this:

M.emplace(k, V{});

where V is the mapped type. It will be value-initialized and moved into the container. (The move might even be elided; I'm not sure.)

If you can't move, and you really need the V to be constructed in-place, you have to use the piecewise construction constructor...

M.emplace(std::piecewise_construct, std::make_tuple(k), std::make_tuple());

This causes std::pair to construct the first element using k and the second element using zero arguments (value-initialization).

Solution 2 - C++

You could explicitly create a pair and pass that to map::emplace, or use the piecewise construction constructor of std::pair.

struct foo {};

std::map<int, foo> m;

m.emplace(std::pair<int, foo>(1, {}));
m.emplace(std::piecewise_construct,
          std::forward_as_tuple(2),
          std::forward_as_tuple());

Live demo

Solution 3 - C++

I had the same problem when I had to create an std::map of std::mutex objects. The issue is that std::mutex is neither copyable nor movable, so I needed to construct it "in place".

The accepted answer doesn't work for this case (M.emplace(k, V{}); needs V to be movable). And I didn't want to use the complicated and less readable std::piecewise_construct option (see above in other answers).

My solution is much simpler - just use the operator[] - it will create the value using its default constructor and return a reference to it. Or it will just find and return a reference to the already existing item without creating a new one.

std::map<std::string, std::mutex> map;

std::mutex& GetMutexForFile(const std::string& filename)
{
    return map[filename]; // constructs it inside the map if doesn't exist
}

Solution 4 - C++

In C++17 you can use std::map::try_emplace, that uses std::piecewise_construct internally and doesn't look that cumbersome. It also takes a key as the first argument (instead of forwarding everything into std::pair::pair() like emplace does).

#include <map>

struct A {
    A() = default;
};

int main()
{
    std::map<int, A> map;

    map.emplace(std::piecewise_construct,
                std::forward_as_tuple(10),
                std::forward_as_tuple());
    // ...vs...
    map.try_emplace(10);
}

Live example.

Solution 5 - C++

Class ToolMap() 
{
 friend class std::map;
  public (std::map)Object ToolMap()
  {
     return Object;
  }

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionInkaneView Question on Stackoverflow
Solution 1 - C++Brian BiView Answer on Stackoverflow
Solution 2 - C++PraetorianView Answer on Stackoverflow
Solution 3 - C++Roman KruglovView Answer on Stackoverflow
Solution 4 - C++Dev NullView Answer on Stackoverflow
Solution 5 - C++mtf-WhitneyView Answer on Stackoverflow