1

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.

2

1 Answer 1

4

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.

2
  • @JeJo but the insert returns nothing. Maybe I misunderstand, but std::vector::insert doesn't return "nothing". It returns an iterator.
    – eerorika
    Commented Aug 8, 2021 at 8:15
  • 1
    @JeJo push_back returns nothing. Anyway thanks for the reminder, that's a new feature I didn't grasp. Commented Aug 8, 2021 at 8:20

Not the answer you're looking for? Browse other questions tagged or ask your own question.