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

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?

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).

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

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
}

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.

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

Related

Construct-in-place an unmoveable object in a map

I'm trying to construct an object in a map that contains an atomic, so it can neither be copied nor moved AFAICT.
My reading of C++ reference is that map emplace should be able to do this. But the following code does not compile because of deleted or non-existent constructors. Using make_pair does not help.
#include <atomic>
#include <unordered_map>
class Z {
std::atomic<int> i;
};
std::unordered_map<int, Z> map;
void test(void) {
map.emplace(0, Z()); // error
map[0] = Z(); // error
}
Is this possible, and if not, why not?
EDIT: Compiler is gcc 4.8.1, on Linux
map.emplace(std::piecewise_construct, std::make_tuple(0), std::make_tuple()) will construct a zero-argument Z at location 0.
map[0] will also do it if it is not already there.
emplace takes the arguments to construct a std::pair<const K, V>. std::pair has a std::piecewise_construct_t tagged constructor that takes two tuples, the first is used to construct the first argument, the second to construct the second argument.
so std::pair<const int, Z> test( std::piecewise_construct, std::make_tuple(0), std::make_tuple() ) constructs the tests elements in-place, the const int is constructed with (0). The Z is constructed with ().
map.emplace forwards is arguments to the std::pair constructor.
The simplest solution is to use operator[] to construct the value inside the map. Then you can assign a value (or operate on it as needed).
May be the following solution will be better, since atomic is not copyable:
class Z {
std::atomic<int> i;
};
std::unordered_map<int, std::shared_ptr<Z>> map;
void test(void) {
map.emplace(0, std::make_shared<Z>()); // OK
}

Map of std::mutex: how to call emplace when constructor takes no arguments [duplicate]

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?
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).
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
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
}
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.
Class ToolMap()
{
friend class std::map;
public (std::map)Object ToolMap()
{
return Object;
}
}

inserting value of a struct to pair in C++

I have read in the C++ reference for map insertion and in the example is shown the following code
std::map<char,int> mymap;
mymap.insert (it, std::pair<char,int>('b',300));
while at the same time I saw a code of a similar operation with
mymap.insert( pair<uuid,structval>( (*it).first, structval() ) );
The structval itself is declared as a normal structure
struct structval {
//some params
};
Can anyone please explain me why to initiate the pair of structval we can simply call the structure by using structval() operation? What does structval() there means? Is this a valid way to access a struct and put it as a pair?
Thank you
What does structval() there means? Is this a valid way to access a struct and put it as a pair?
structval() instantiates a temporary object of type structval. Therefore, technically, it is not a way to access a struct, but to create an instance of it. And yes, it is a valid way to create an instance of an object:
struct X
{
X() : i_(42) { }
int i_;
};
void foo(X x) { /* ... */ }
// ...
foo(X()); // <== Valid way to construct a temporary object
// of type X and pass it as an argument to
// the foo() function.
This temporary, default-constructed instance is then passed as an argument to the constructor of std::pair (and by the way, the pair object you are constructing is also a temporary!).
Usually, pairs are constructed by calling the std::make_pair() function, that can deduce the type of the arguments for you, so you don't have to type them explicitly. Therefore, you could rewrite this:
pair<uuid,structval>( (*it).first, structval() )
As the following:
std::make_pair((*it).first, structval())
In C++11 you could also use the emplace member function instead of insert():
mymap.emplace((*it).first, structval());
This would make it unnecessary to explicitly construct the pair (or to invoke std::make_pair() for that purpose).
As a final remark, the value type of a map is not:
pair<key_type, mapped_type>
But rather:
pair<key_type const, mapped_type>
// ^^^^^
Because a map's keys shall never be changed, in order to prevent messing up with the internal ordering of the container.
structval() is a call of constructor of type structval without parameters. So, simply use temporary created object.
Related: you should use std::make_pair, instead of call pair c-tor in most cases.

std::map<>::insert using non-copyable objects and uniform initialization

Have a look at the following code:
#include <utility>
#include <map>
// non-copyable but movable
struct non_copyable {
non_copyable() = default;
non_copyable(non_copyable&&) = default;
non_copyable& operator=(non_copyable&&) = default;
// you shall not copy
non_copyable(const non_copyable&) = delete;
non_copyable& operator=(const non_copyable&) = delete;
};
int main() {
std::map<int, non_copyable> map;
//map.insert({ 1, non_copyable() }); < FAILS
map.insert(std::make_pair(1, non_copyable()));
// ^ same and works
}
Compiling this snippet fails when uncommenting the marked line on g++ 4.7. The error produced indicates that non_copyable can't be copied, but I expected it to be moved.
Why does inserting a std::pair constructed using uniform initialization fail but not one constructed using std::make_pair? Aren't both supposed to produce rvalues which can be successfully moved into the map?
[This is a complete rewrite. My earlier answer had nothing to do with the problem.]
The map has two relevant insert overloads:
insert(const value_type& value), and
<template typename P> insert(P&& value).
When you use the simple list-initializer map.insert({1, non_copyable()});, all possible overloads are considered. But only the first one (the one taking const value_type&) is found, since the other doesn't make sense (there's no way to magically guess that you meant to create a pair). The first over­load doesn't work of course since your element isn't copyable.
You can make the second overload work by creating the pair explicitly, either with make_pair, as you already described, or by naming the value type explicitly:
typedef std::map<int, non_copyable> map_type;
map_type m;
m.insert(map_type::value_type({1, non_copyable()}));
Now the list-initializer knows to look for map_type::value_type constructors, finds the relevant mova­ble one, and the result is an rvalue pair which binds to the P&&-overload of the insert function.
(Another option is to use emplace() with piecewise_construct and forward_as_tuple, though that would get a lot more verbose.)
I suppose the moral here is that list-initializers look for viable overloads – but they have to know what to look for!
Besides other answers of providing move (assignment) constructor, you could also store the non-copyable object through pointer, especially unique_ptr. unique_ptr will handle resource movement for you.

Moving an object into a map

The problem with this is that the huge objects will be copied into the maps
Huge huge1(some,args);
Huge huge2(some,args);
std::map<int,Huge> map1;
std::map<Huge,int> map2;
map1.insert({0,huge1});
map2.insert({huge2,0});
how can I guarantee a move? Will this work or is there more to it?
map1.insert({0,std::move(huge1)});
map2.insert({std::move(huge2),0});
std::map::insert has an overload for R-values:
std::pair<iterator,bool> insert(value_type&&);
Any expression which binds to this overload will invoke R-value constructors. Since std::map<K,V>::value_type is std::pair<const key_type, mapped_type>, and std::pair has a constructor that takes R-values:
template<class U1, class U2>
pair(U1&& x, U2&& y);
then you are guaranteed that R-value constructors for key_type and mapped_type will be invoked, both in the creation of the pair object, and in the map insertion, as long as you insert the pair using an expression that creates R-values, such as:
map1.insert(std::make_pair(0, Huge());
OR
map1.insert(std::make_pair(0, std::move(huge1));
Of course, all of this is dependent on Huge having a proper R-value constructor:
Huge(Huge&& h)
{
...
}
Finally, you can also use std::map::emplace if you simply want to construct a new Huge object as an element in the map.
You could do that (the {0,std::move(huge1)} part). But you could also skip the middleman (assuming you're constructing the objects within the function) like this:
map1.emplace(std::piecewise_construct, 0, std::forward_as_tuple(some, args));
map2.emplace(std::piecewise_construct, std::forward_as_tuple(some, args), 0);
Or, if your function is given the objects, you can still use emplace:
map1.emplace(0, std::move(huge1));
map2.emplace(std::move(huge1), 0);
An alternative that avoids both copying and moving would be to use std::map::emplace(). From the linked reference page:
Inserts a new element to the container. The element is constructed in-place, i.e. no copy or move operations are performed. The constructor of the element type (value_type, that is, std::pair) is called with exactly the same arguments as supplied to the function, forwarded with std::forward(args)....
Along with the above, you can also rely on std::unique_ptr<>'s lack of a copy constructor, though this changes the interface slightly.
#include <iostream>
#include <map>
#include <memory>
class Huge {
public:
Huge(int i) : x{i} {}
int x;
};
using HugePtrT = std::unique_ptr<Huge>;
using MyMapT = std::map<int, HugePtrT>;
int
main() {
MyMapT myMap;
myMap[42].reset(new Huge{1});
std::cout << myMap[42]->x << std::endl;
myMap[43] = std::move(myMap[42]);
if (myMap[42])
std::cout << "42: " << myMap[42]->x << std::endl;
if (myMap[43])
std::cout << "43: " << myMap[43]->x << std::endl;
}
which produces the expected output:
1
43: 1
If you omit the std::move() call, the program will fail to compile. Similarly, you could use .reset() to assign the pointer.
This has the advantage that it will work on classes that don't have an R-value constructor, is very light weight, memory ownership is clearly defined, and gives you a boost::optional<>-like semantics. An argument could be made that std::unique_ptr is lighter weight than an object that has been moved via an R-value constructor because an R-Value moved object requires an allocation (though to be fair, all of the C++11 compilers that I'm aware of support Return Value Optimizations or copy elision), even though the guts of the object are moved.
The reason std::unique_ptr<> works like this is because std::unique_ptr<> does not have a copy constructor, it only has a move constructor.