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 overload 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 movable 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.
Related
I get this strange compilation error on new versions of gcc (4.9+).
Here is the code:
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <map>
using namespace std;
struct ptrwrap
{
unique_ptr<int> foo;
};
template <typename T>
struct holder
{
holder() = default;
holder(const holder& b)
: t(b.t)
{
}
holder(holder&& b)
: t(std::move(b.t))
{
}
holder& operator=(const holder& h)
{
t = h.t;
return *this;
}
holder& operator=(holder&& h)
{
t = std::move(h.t);
return *this;
}
T t;
};
struct y_u_no_elision
{
holder<ptrwrap> elem;
};
typedef map<std::string, y_u_no_elision> mymap;
mymap foo();
int main()
{
auto m = foo();
m = foo();
return 0;
}
Here it is on ideone too with the actual error. Basically it boils down to using the deleted copy constructor of ptrwrap. Which... shouldn't happen. The map is returned by value (ie moved), so no copies can exist.
Now the same code is compiled with no problems on older versions of gcc (I tried 4.2 and 4.3), all versions of clang I tried, and also Visual Studio 2015.
Curiously if I remove the explicit copy and move constructors of the holder template, it also compiles on gcc 4.9+. If I change the map to a vector or an unordered_map it also compiles fine (here is a link to a compiling version of the code with unordered_map)
So... is this a gcc 4.9 bug or are the other compilers being permissive of something I can't see? Is there anything I can do about this which doesn't involve changing the holder class?
Short answer: It's a bug in libstdc++. According to the allocator-aware container requirements table in [container.requirements.general] in the Standard (hasn't changed since C++11), container move assignment:
Requires: If allocator_traits<allocator_type>::propagate_on_container_move_assignment::value
is false, T is MoveInsertable into X and MoveAssignable.
[...]
(X is the container type and T is its value_type)
You're using the default allocator, which has using propagate_on_container_move_assignment = true_type;, so the above requirement doesn't apply; there should be no special requirements on the value_type.
Quick fix: If you cannot touch holder, one solution is to change y_u_no_elision, adding
y_u_no_elision(const y_u_no_elision&) = delete;
y_u_no_elision(y_u_no_elision&&) = default;
Long story: The bug is essentially caused by this line in stl_tree.h.
_Rb_tree is the underlying implementation of std::map and that line in its move assignment operator definition basically does the check specified by the Standard quote above. However, it does it using a simple if, which means that, even if the condition is satisfied, the other branch has to compile as well, even though it won't be executed at runtime. Lacking the shiny new C++17 if constexpr, this should be implemented using something like tag dispatching (for the first two conditions - the third one is a true runtime check), in order to avoid instantiating the code outside the taken branch.
The error is then caused by this line, which uses std::move_if_noexcept on value_type. And here comes the long story.
value_type is std::pair<const std::string, y_u_no_elision>.
In your initial code:
holder has non-deleted, non-noexcept copy and move constructors.
This means the corresponding implicitly-declared constructors of y_u_no_elision will also be non-deleted and non-noexcept.
These characteristics propagate to the constructors of value_type.
This results in std::move_if_noexcept returning const value_type& instead of value_type&& (it falls back to copy if it can - see these docs).
That eventually causes the copy constructor of y_u_no_elision to be called, which will cause holder<ptrwrap>'s copy constructor definition to be instantiated, which tries to copy a std::unique_ptr.
Now, if you remove the user-declared copy and move constructors and assignment operators from holder:
holder will get the implicitly-declared ones. The copy constructor will be deleted and the move constructor will be defaulted, not deleted and noexcept.
This propagates up to value_type, with one exception unrelated to holder: value_type's move constructor will try to move from a const std::string; this will not call string's move constructor (which is noexcept in this case), but rather its copy constructor, as string&& cannot bind to an rvalue of type const string.
string's copy constructor is not noexcept (it may have to allocate memory), so value_type's move constructor won't be either.
So, why does the code compile? Because of the logic behind std::move_if_noexcept: it returns an rvalue reference even if the argument's move constructor isn't noexcept, as long as the argument is not copy-constructible (it falls back to non-noexcept move if it cannot fall back to copy); and value_type isn't, because of holder's deleted copy constructor.
This is the logic behind the quick fix above: you have to do something to make value_type have a valid move constructor and a deleted copy constructor, in order to get an rvalue reference from move_if_noexcept. This is because you won't be able to make value_type have a noexcept move constructor due to the const std::string, as explained above.
The holder copy constructor invokes t copy constructor:
holder(const holder& b)
: t(b.t)
{
}
If t is a unique_ptr then it doesn't support a construction by copy.
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;
}
}
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;
}
}
I want to use vector::emplace to default construct a non-copyable and non-assignable object and then use specific methods on the object using an iterator to the newly created object. Note that there are no parameterized constructors of the class just the default constructor. A simple example is:
#include <iostream>
#include <vector>
using namespace std;
class Test {
public:
Test() {}
private:
Test(const Test&) = delete; // To make clas un-copyable.
Test& operator=(const Test&) = delete;
int a_;
};
int main() {
vector<Test> test_vec;
test_vec.emplace_back(); // <---- fails
return 0;
}
vector::emplace() constructs a new object but requires arguments to a non-default constructor. vector::emplace_back() will construct at the end of the vector.
Is there a way to emplace with default construction. Is there a way to use piecewise construction or default forwarding perhaps using std::piecewise_construct as there is for maps? For example, in the case of maps, we can use:
std::map<int,Test> obj_map;
int val = 10;
obj_map.emplace(std::piecewise_construct,
std::forward_as_tuple(val),
std::forward_as_tuple());
Is there something similar for vectors?
vector::emplace_back() will construct at the end of the vector but also require arguments.
Parameter packs can be empty. Thus the variadic template emplace_back can be called without arguments; I.e.
vector<VeryLimitedClass> vec;
vec.emplace_back();
Is valid code that initializes an object of type VeryLimitedClass through its default constructor and "emplaces" it at the back of vec.
As pointed out by #dyp and #Casey in the comments, std::emplace will not work for vectors of the Test class as the class is neither movable nor copyable because "the user-declared copy constructor suppresses generation of the default move constructor" (#Casey).
To use emplace here, the class will need to be movable or copyable. We can make the class movable by explicitly defining (and defaulting) the move constructors:
public:
Test(Test&& other) = default;
Test& operator=(Test&& other) = default;
This will also implicitly make the class not-copyable "since declaring the move operations will suppress implicit generation of the copies." (#Casey)
Now we can use std::emplace_back() and then use vector::back() to call methods of the newly created object.
For map, easy:
std::map<int, Object> obj_map;
obj_map[10]; // default-constructs an object with key 10
Otherwise, what you have works too:
obj_map.emplace(std::piecewise_construct,
std::forward_as_tuple(10),
std::forward_as_tuple(args, to, construct, with));
[edit] The equivalent for vector is emplace_back:
obj_vector.emplace_back(); // default construct
obj_vector.emplace_back(args, to, std::move(construct), with); // forward these
Suppose you want to take advantage of move semantics, but one of your movable classes needs to be part of an std::pair. The purpose would be to create a function that returns an std::pair that can be treated as an rvalue, and forwarded along.
But I can't see how this can be done, unless an internal change to std::pair itself is made, to make it aware of move semantics.
Consider the following code:
struct Foo
{
Foo() { }
Foo(Foo&& f) { }
private:
Foo(const Foo& f) { } // do not allow copying
};
int main()
{
Foo f;
std::pair<Foo, int> res = std::make_pair(f, 10); // fails due to private copy constructor
}
The problem is that std::make_pair, as well as the std::pair constructor itself, takes two objects and tries to make internal copies of them. This causes it to try and invoke the copy constructor. But in my example, I want to be able to move the new pair into res, and ensure that no copies are made. I would think this wouldn't be possible unless std::pair itself had the following constructor defined internally:
pair(T1&& t1, T2&& t2) : first(std::move(t1)), second(std::move(t2))
But it doesn't, at least not on the compiler I'm using (gcc 4.3.2). It may be that my compiler is simply out-of-date, and newer versions in fact will have this move-aware constructor. But my understanding of move semantics is somewhat flaky at the moment, so I'm not sure if I'm simply overlooking something here. So, is what I'm trying to accomplish possible, without actually reimplementing std::pair? Or is my compiler just out of date?
That's not the std::pair constructor that will be called, though. The std::pair move constructor will be called, and the move constructor should do exactly what you expect (N3126 20.3.5.2/6):
template<class U, class V> pair(pair<U, V>&& p);
Effects: The constructor initializes first with std::move(p.first) and second with std::move(p.second).
However, your example should fail because in std::make_pair(f, 10);, f is an lvalue and needs to be explicitly moved, otherwise it is copied. The following should work:
std::pair<Foo, int> res = std::make_pair(std::move(f), 10);
GCC 4.3.2 must not have a complete implementation. pair (and tuple) shall have move constructors:
template<class U, class V> pair(U&& x, V&& y);
Effects: The constructor initializes first with std::forward(x) and second with std::forward(y).
template<class U, class V> pair(pair<U, V>&& p);
Effects: The constructor initializes first with std::move(p.first) and second with std::move(p.second).
(From [pairs.pair] in n3126)