C ++ 11 - emplace_back between two vectors not working

I tried to adapt some code and move content from vector to another using emplace_back()

 #include <iostream> #include <vector> struct obj { std::string name; obj():name("NO_NAME"){} obj(const std::string& _name):name(_name){} obj(obj&& tmp): name(std::move(tmp.name)) {} obj& operator=(obj&& tmp) = default; }; int main(int argc, char* argv[]) { std::vector<obj> v; for( int i = 0; i < 1000; ++i ) { v.emplace_back(obj("Jon")); } std::vector<obj> p; for( int i = 0; i < 1000; ++i ) { p.emplace_back(v[i]); } return(0); } 

This code does not compile with g ++ - 4.7, g ++ - 4.6 and clang ++: what is wrong with it?

I always got 1 main error about

invoking an implicitly remote copy of the obj constructor

?

+7
source share
2 answers

Although the existing answer provides a workaround using std::move that forces your program to compile, it must be said that using emplace_back seems to be based on a misunderstanding.

The way you describe it ("I tried [...] to move content from a vector to another using emplace_back() "), and the way you use it assumes that you think of emplace_back as a method of moving items in vector and push_back as a method for copying elements to a vector. The code you use to populate the first instance of the vector also offers this:

 std::vector<obj> v; for( int i = 0; i < 1000; ++i ) { v.emplace_back(obj("Jon")); } 

But it's not that the difference between emplace_back and push_back about equal.

Firstly, even push_back will move (not copy) elements to a vector if it is only set to r, and if the element type has a move assignment operator.

Secondly, the real use emplace_back for emplace_back is to build elements in place , i.e. you use it when you want to put objects in a vector that does not exist yet. The arguments emplace_back are arguments to the constructor of the object. So, your loop above should look like this:

 std::vector<obj> v; for( int i = 0; i < 1000; ++i ) { v.emplace_back("Jon"); // <-- just pass the string "Jon" , not obj("Jon") } 

The reason your existing code works is because obj("Jon") also a valid argument for the constructor (in particular, for the move constructor). But the main idea of emplace_back is that you do not need to create an object and then move it. You do not use this idea when you pass it obj("Jon") instead of "Jon" .

On the other hand, in the second cycle, you are dealing with objects that were created earlier. It makes no sense to use emplace_back to move objects that already exist. Again, emplace_back applied to an existing object does not mean that the object has been moved. This means that it is created in place using the regular copy constructor (if one exists). If you want to move it, just use push_back applied to the result of std::move :

 std::vector<obj> p; for( int i = 0; i < 1000; ++i ) { p.push_back(std::move(v[i])); // <-- Use push_back to move existing elements } 

Additional notes
1) You can simplify the loop above by using the C ++ 11 range for:

 std::vector<obj> p; for (auto &&obj : v) p.push_back(std::move(obj)); 

2) Regardless of whether you use the usual for-loop or range-based method, you move the elements one by one, which means that the source vector v will remain as a vector of 1000 empty objects. If you really want to clear the vector in the process (but still use the move semantics to transfer the elements to the new vector), you can use the move constructor of the vector itself:

 std::vector<obj> p(std::move(v)); 

This reduces the second loop to one line and ensures that the original vector is deleted.

+9
source

The problem is

 p.emplace_back(v[i]); 

passes the lvalue to emplace_back , which means that your move constructor (which expects the rvalue link) will not work.

If you really want to move values ​​from one container to another, you must explicitly call std::move :

 p.emplace_back(std::move(v[i])); 

(The idea of ​​a move constructor, such as obj(obj&& tmp) , is that tmp should be an object that won't be used much longer. In your first loop, you pass a temporary object emplace_back , which is exact - the rvalue reference can bind to a temporary object and steal data from it because the temporary object is about to disappear.In your loop, the second object that you go to emplace_back has the name: v[i] . This means that it is not temporary and can be transferred later in the program. Therefore, you should use std::move , to coo schit compiler "Yes, I really wanted to steal the data from this object, even if someone else may try to use it later.")


Edit: I assume that your rather unusual use of emplace_back is a relic of creating a small example for us. If not, see the @jogojapan Comment for a good discussion on why using the std::vector move constructor or push_back callbacks would make more sense for your example.

+7
source

All Articles