Reputation: 81
I knew this has been asked quite a lot, and I have seen many explanations quoting "emplace_back construct in place, push_back() construct and copy". Some posts asked why emplace_back calls the copy constructor because they did not reserve the memory for the vector.
But for the below case, I cannot figure out what emplace_back() achieve more than push_back(). Some answer says "you need to implement a move constructor for emplace_back() to work" But push_back() can also utilize the move constructor. So what is the difference
#include <iostream>
#include <vector>
using namespace std;
class Int{
public:
int* p;
Int(int x): p(new int(x)) {cout<<"constructor called for "<<*p<<endl;}
Int(const Int& x): p(new int(*(x.p))) {cout<<"copy constructor called for "<<*p<<endl;}
~Int(){
if (p!= nullptr) {cout<<"destructor called for "<<*p<<endl;}
else{cout<<"destructor called for null ptr"<<endl;}
delete p;
}
Int(Int&& x): p(x.p) {
x.p = nullptr;
cout<<"move constructor called for "<<*p<<endl; // move constructor, remove to test emplace_back()
}
};
int main(){
vector<Int> v;
v.reserve(1);
v.emplace_back(Int(1)); // can switch to push_back()
// v.push_back(Int(1));
cout<<"end program"<<endl;
}
For me, it seems both methods call copy constructor without move constructor, and calls move constructor if there is one.
Upvotes: 1
Views: 1780
Reputation: 172884
emplace_back
constructs the element in-place by forwarding the arguments to the constructor of element type, so you can
v.emplace_back(1); // forwarding 1 to Int::Int(int) to construct the element directly
push_back
always expects an element, i.e. an Int
. When you pass 1
to it as v.push_back(1);
, implicit conversion happens. A temporary Int
is constructed from 1
by (Int::Int(int)
then passed to push_back
, the element is constructed from the temporary by the move constructor of Int
later. I.e. one more move constructor invocation than v.emplace_back(1);
.
You can also pass an Int
to emplace_back
like v.emplace_back(Int(1));
, as explained above, the temporary Int
is forwarded to Int
's move constructor to construct the element, which does the same thing as v.push_back(Int(1));
.
As @JeJo suggested, there's another difference between emplace_back
and push_back
since C++17. emplace_back
returns a reference to the inserted element while push_back
returns nothing.
Upvotes: 4