Introduction: why is this important?
\n\nHave you ever wondered why there are two ways to add an element to the end of a vector in C++? It might seem like reinventing the wheel when there's the tried-and-true push_back. The answer lies in the performance and flexibility of modern C++.
On Stack Overflow, this question has gathered thousands of views and hundreds of comments. And this is no coincidence: understanding the difference between push_back and emplace_back is not just academic knowledge, but a practical skill that can speed up your code by 2-3 times. Let's figure it out once and for all.
Basic difference: what do the docs say?
\n\nLet's start simple. Both methods add an element to the end of a container (vector, list, queue). But they do it differently:
\n\n- \n
push_back— takes a ready-made object (by value, reference, or rvalue reference) and copies/moves it into the container. \n emplace_back— takes arguments for the object's constructor and creates it directly inside the container (in-place construction). \n
Sounds abstract? Let's look at the code.
\n\nPractical example: creating a class
\n\nLet's imagine we have a simple class Person:
#include <iostream>\n#include <vector>\n#include <string>\n\nclass Person {\npublic:\n std::string name;\n int age;\n\n // Constructor\n Person(const std::string& n, int a) \n : name(n), age(a) {\n std::cout << "Constructor called for " << name << std::endl;\n }\n\n // Copy constructor\n Person(const Person& other) \n : name(other.name), age(other.age) {\n std::cout << "Copy constructor for " << name << std::endl;\n }\n\n // Move constructor\n Person(Person&& other) noexcept\n : name(std::move(other.name)), age(other.age) {\n std::cout << "Move constructor for " << name << std::endl;\n }\n};\n\nNow let's see what happens when using different methods.
\n\nOption 1: push_back with a temporary object
\n\nint main() {\n std::vector<Person> people;\n people.push_back(Person("Alice", 25));\n return 0;\n}\n\nOutput:
\nConstructor called for Alice\nMove constructor for Alice\n\nWhat happened? First, a temporary Person object is created (first constructor call). Then this object is moved into the vector (move constructor). Two calls — double the work.
Option 2: push_back with a ready-made object
\n\nint main() {\n std::vector<Person> people;\n Person alice("Alice", 25);\n people.push_back(alice); // alice — lvalue\n return 0;\n}\n\nOutput:
\nConstructor called for Alice\nCopy constructor for Alice\n\nHere we see copying, not moving. If the object is large (contains, for example, dynamic arrays), copying can be expensive.
\n\nOption 3: emplace_back
\n\nint main() {\n std::vector<Person> people;\n people.emplace_back("Bob", 30); // Pass arguments for the constructor\n return 0;\n}\n\nOutput:
\nConstructor called for Bob\n\nOnly one constructor call! The Person object is created directly inside the vector's memory, without intermediate copies or moves.
Performance: when is the difference critical?
\n\nFor simple types (int, double, pointers) the difference is negligible — the compiler optimizes everything. But for complex objects, the difference can be significant:
\n\n- \n
- Objects with a heavy copy constructor (e.g.,
std::string, containers