I created a vector and used push_back to put several node objects into it. However, I can't predict when its going to use the move constructor or copy constructor.
Is there any pattern to when push_back use copy constructor or move constructor? c++ reference says that it'll sometimes copy and sometimes move, but never went into detail as to when they do what.
#include <iostream>
#include <vector>
#include <unordered_set>
#include <set>
#include <unordered_map>
#include <map>
#include <queue>
using namespace std;
struct Node {
int val;
Node(int val) : val(val) {
cout<<"created object" << val<<endl;
}
Node(const Node& m) : val(m.val) {
cout<<"copy constructor is called on value " << m.val << endl;
}
~Node() {
cout<<"destroyed val" << val<<endl;
}
Node(Node&& other) noexcept
: val(other.val) {
cout<<"moved val " << other.val << endl;
}
};
void f(vector<Node>& a) {
cout<<"______________________"<<endl;
Node tmp(12);
cout<<"12 established"<<endl;
a.push_back(tmp);
cout<<"a pushed back 12"<<endl;
a.push_back(Node(14));
cout<<"a pushed back tmp obj 14"<<endl;
tmp.val+=5;
cout<<"increased tmp.val"<<endl;
cout<<tmp.val<<endl;
cout<<a[1].val<<endl;
cout<<"two prints"<<endl;
cout<<"_______________"<<endl;
cout<<"end of f"<<endl;
// return a;
}
int main() {
vector<Node> a = {Node(125)}; //copied since initialized temp var.
a.reserve(4000);
cout<<"start of f"<<endl;
f(a);
cout<<"program ended"<<endl;
//noteiced: Copy constructor called upon local variable (12) that the vector knows will not stay with it --- and belongs to local scope.
//copy constructor not called upon temporary variable that soon belonged to vector (14).
//same thing with std::queue, std::stack, and many others.
//but it this a pattern or a coincidence?
}
Output:
copy constructor is called on value 125
destroyed val125
moved val 125
destroyed val125
start of f
______________________
created object12
12 established
copy constructor is called on value 12
a pushed back 12
created object14
moved val 14
destroyed val14
a pushed back tmp obj 14
increased tmp.val
17
12
two prints
_______________
end of f
destroyed val17
program ended
destroyed val125
destroyed val12
destroyed val14```
The rules are very simple.
std::vector::push_back
has two overloads.
The first overload takes a const T &
parameter. Invoking that overload uses the object's copy constructor.
The second overload takes a T &&
parameter. Invoking that overload uses the object's move constructor.
Note that both overloads may end up reallocating the vector, which will trigger a whole bunch of moves, which is besides the point. I haven't mapped out all of your code's diagnostic output, but it's also possible that you're logging moves due to reallocation and this further muddles the issue (although I see that you used reserve()
to get rid of this, the reserve()
is on a non-empty vector, so its sole existing value gets moved, and you might be getting confused by that, too).
And all of this assumes that T
implements move semantics. If it doesn't, it's copies all the way.
So what you have to do is simply determine whether a given particular invocation of push_back()
passes an lvalue or an rvalue.
a.push_back(tmp);
tmp
is obviously an lvalue. This will end up invoking the copy constructor.
a.push_back(Node(14));
This parameter is an rvalue. This will end up invoking the move constructor.
You can work out the rest of the invocations using the same principle.