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_backhas two overloads.The first overload takes a
const T ¶meter. Invoking that overload uses the object's copy constructor.The second overload takes a
T &¶meter. 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, thereserve()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
Timplements 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.tmpis obviously an lvalue. This will end up invoking the copy constructor.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.