Let’s try one thing

Do me a favour. Open up a project, any project, and search for all the occurrences of (std::vector)push_back.

Done? Write down the number of results.

Now do the same with emplace_back. Compare the results.

I’m ready to bet that the first search gave way more results than the second one. Why?

The reason might be historic (push_back has been around since forever, while emplace_back is a C++11 addition), and there might even be a good reason for that (more of that later). But usually the real reason is simple and a bit saddening: people have been thought to use push_back, or started using C++ before emplace_back existed, and they never learned why they should use emplace_back.

So, what’s the difference?

This is the signature of push_back:

void push_back( const T& value ); (1)
void push_back( T&& value )       (2)

while emplace_back looks like this (until C++17):

template< class... Args >
void emplace_back( Args&&... args );

If you are familiar with C++ (and variadic templates) you should have already spotted the difference. While push_back takes a value and appends it to the container (possibly copying it), emplace_back gets a parameter pack and forwards it to the constructor.

What does that mean in practice?

It means that push_back will potentially have to create an object and then copy it to the vector, while emplace_back will forward the arguments to the constructor and create the object directly inside.

Let’s try it on a piece of code

#include <iostream>
#include <vector>

int emptyConstructor = 0;
int normalConstructor = 0;
int copyConstructor = 0;

class Person
{
private:
    std::string firstName_, lastName_;
public:
    Person() {
        ++emptyConstructor;
    }

    Person(std::string firstName, std::string lastName):
        firstName_(firstName),
        lastName_(lastName)
    {
        ++normalConstructor;
    }

    // Copy constructor
    Person(const Person& p2) {
        firstName_ = p2.firstName_;
        lastName_ = p2.lastName_;
        ++copyConstructor;
    }
};

int main()
{
    const int N = 40000;
    std::vector<Person> person1;
    person1.reserve(N);
    for (int i = 0; i < N; i++) {
        person1.push_back(Person("firstName", "lastName"));
    }
    
    std::cout << "--------- push_back results ----------\n";
    std::cout << "Empty constructor called " << emptyConstructor << " times.\n";
    std::cout << "Normal constructor called " << normalConstructor << " times.\n";
    std::cout << "Copy constructor called " << copyConstructor << " times.\n";

    emptyConstructor = 0;
    normalConstructor = 0;
    copyConstructor = 0;

    std::vector<Person> person2;
    person2.reserve(N);
    for (int i = 0; i < N; i++) {
        person2.emplace_back("firstName", "lastName");
    }
    
    std::cout << "\n\n--------- emplace_back results ----------\n";
    std::cout << "Empty constructor called " << emptyConstructor << " times.\n";
    std::cout << "Normal constructor called " << normalConstructor << " times.\n";
    std::cout << "Copy constructor called " << copyConstructor << " times.\n";

    return 0;
}

If you run this code, you will get the following result:

--------- push_back results ----------
Empty constructor called 0 times.
Normal constructor called 40000 times.
Copy constructor called 40000 times.


--------- emplace_back results ----------
Empty constructor called 0 times.
Normal constructor called 40000 times.
Copy constructor called 0 times.

(see the code in action and play with it here)

Since push_backis receiving a temporary object, it needs to copy it into the vector, so each call to push_backactually implies two object constructions: the first one is at Person("firstName", "lastName")), and the second one is the one of the copy constructor that is called inside push_backto copy the object inside the vector.

emplace_back, on the other side, is called by directly passing it the arguments that get forwarded to the constructor of Person, thus avoiding the need for the copy constructor to be called!

C++17 addendum

There is another nice, little reason to prefer emplace_back over push_back, and it is a change to its signature since C++17.

From

template< class... Args >
void emplace_back( Args&&... args );

to

template< class... Args >
reference emplace_back( Args&&... args );

The function now returns a reference to the inserted object. That allows us some nice chaining, as we can now access an object we just inserted, from a temporary, inside our vector, without having to pass through std::vector::back().

As an example, let’s add to our Personclass a method to change its first name:

  void changeFirstName(std::string newName){
        firstName_ = newName;
    }

And suppose that, for whatever reason, we want to change the first name of each object we insert, right after inserting. If we are using push_backthere is no better way do that than:

for (int i = 0; i < N; i++) {
    person1.push_back(Person("firstName", "lastName"));
    person1.back().changeFirstName("newFirstName");
}

while emplace_backgives us a better, cleaner alternative:

for (int i = 0; i < N; i++) {
    person2.emplace_back("firstName", "lastName").changeFirstName("newName");
}

(code here)

So, should I completely ditch push_back in favour of emplace_back?

Well..

If you do that, I would say you will be completely fine 99% of the cases… however there are some cases in which emplace_backmight not be the correct choice, and that would mostly come down to implicit vs explicit constructors… something we might want to talk in a future post.


Nikolas De Giorgis

Basically, a software developer. Less basically, I'm passionate about photography, music, sport, reading, traveling and many, many other things!