[Modern C++ Series] vector push_back or emplace_back ??

JustIdeas
4 min readJan 13, 2023

--

When it comes to inserting elements into a C++ vector, you have the choice between using push_back and emplace_back. Both functions are designed to do the same thing, but they work in slightly different ways, and depending on the situation, one may be more appropriate than the other.

push_back” is similar to adding a new toy to your toy box in that you already have the item in your hand and just set it in the box. It duplicates the element and then adds it to the vector. It’s a safe bet, and you should use it when you already have a copy of the element and the item is inexpensive to copy.

A kid is adding an exisiting toy to his collection. (push_back)

emplace_back”, on the other hand, is like making a toy from scratch; you have all the parts and instructions, and you put it together within the toy box. It builds the element within the vector rather than copying it. It is a more efficient option, and you should use it when the object is expensive to replicate or when you only have the arguments to construct the object.

Kid is building the toy from scratch using arguments in place.

You should use push_back when:

  1. You already have a copy of the element that you want to insert into the vector.
  2. The object you want to insert is cheap to copy.
  3. The object has a non-explicit copy constructor

You should use emplace_back when:

  1. You have the arguments to construct the object, but not the object itself.
  2. The object is expensive to copy.
  3. The object has a non-explicit constructor that takes the same arguments as you want to pass to emplace_back

In general, use emplace back when building an element in place. When we need to build an object straight inside the container, we should use emplace back() since it will directly generate an object within the container, eliminating the need for a move/copy ().

Why not use emplace_back everywhere ??

Using emplace_back everywhere instead of push_back might sound like a great idea, but it's not always the best choice. With current implementations of the Standard Library, there are situations where, as expected, emplacement outperforms insertion. But, sadly, there are also situations where the insertion functions run faster. Such situations are not easy to characterize, because they depend on the types of arguments being passed, the containers being used, the locations in the containers where insertion or emplacement is requested etc.

I understand this is not very satisfying. But there are situations where emplacement functions are most likely to outperform their insertion counterpart-

  1. The value being added is constructed into the container, not assigned.
  2. The argument type(s) being passed differ from the type held by the container.
  3. The container is unlikely to reject the new value as a duplicate.

In short, you can’t use emplace_back every where instead of push_back because it's not always the most efficient or convenient option [ Don’t believe me yet? Check out this code and be amazed!]. Just like making a toy, you have to weigh the pros and cons of each method and choose the one that suits your needs the best.

Performance measure using code snippet-

#include <iostream>
#include <vector>
#include <chrono>

struct Test{
int a,b;
Test(int x, int y) : a(x), b(y) {}
};

int main() {
std::vector<Test> v1;
std::vector<Test> v2;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 100000; ++i) {
v1.push_back(Test(i, i));
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << "push_back took "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
<< " microseconds\n";

start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 100000; ++i) {
v2.emplace_back(i, i);
}
end = std::chrono::high_resolution_clock::now();
std::cout << "emplace_back took "
<< std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
<< " microseconds\n";

return 0;
}

Output -

push_back took 13431 microseconds
emplace_back took 10031 microseconds

Ever wonder why a compiler chooses to copy elements rather than relocate/move them when a vector’s size is more than its capacity? Comment your answer down below -

--

--

JustIdeas

https://topmate.io/cppelite -- Engineer by day, jokester by night. I'm always looking for ways to make the world a better place, one laugh at a time!