Construct-in-place an unmoveable object in a map - c++

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
}

Related

Assigning of unordered_map to pair of objects

I am trying to understand the unordered_map assignment, I get the following error: no matching function for call to std::pair<foo, foo>::pair(), according to the doc for unordered_map operator[]:
If k does not match the key of any element in the container, the function inserts a new element with that key and returns a reference to its mapped value.
So I am trying to assign an object (from make_pair) to this reference, which I am guessing is not allowed. However with the pair<int,int>, it works, then I am wondering if I must declare some other operators for foo to make this work.
#include <bits/stdc++.h>
using namespace std;
struct foo {
int n;
foo(int n): n(n) {};
};
int main(){
unordered_map<int, pair<foo,foo>> m;
//m[3] = make_pair(foo(1),foo(2)); <--- error here
unordered_map<int, pair<int,int>> ii;
ii[3] = make_pair(1,2);
}
The problem is that operator [] might have to construct a value object, in your case std::pair<foo, foo>. Since foo doesn't have a default constructor, it can't construct the default std::pair.
You can either provide a default constructor for foo (including adding a default value for n), or you'll have to use another method to insert values into m.

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;
}
}

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

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.