push_back vs emplace_back: what's the difference and what should a beginner choose?

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: why is this important?

\n\n

Have 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++.

\n\n

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.

\n\n

Basic difference: what do the docs say?

\n\n

Let'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
\n\n

Sounds abstract? Let's look at the code.

\n\n

Practical example: creating a class

\n\n

Let's imagine we have a simple class Person:

\n\n
#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\n

Now let's see what happens when using different methods.

\n\n

Option 1: push_back with a temporary object

\n\n
int main() {\n    std::vector<Person> people;\n    people.push_back(Person("Alice", 25));\n    return 0;\n}
\n\n

Output:

\n
Constructor called for Alice\nMove constructor for Alice
\n\n

What 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.

\n\n

Option 2: push_back with a ready-made object

\n\n
int main() {\n    std::vector<Person> people;\n    Person alice("Alice", 25);\n    people.push_back(alice);  // alice — lvalue\n    return 0;\n}
\n\n

Output:

\n
Constructor called for Alice\nCopy constructor for Alice
\n\n

Here we see copying, not moving. If the object is large (contains, for example, dynamic arrays), copying can be expensive.

\n\n

Option 3: emplace_back

\n\n
int main() {\n    std::vector<Person> people;\n    people.emplace_back("Bob", 30);  // Pass arguments for the constructor\n    return 0;\n}
\n\n

Output:

\n
Constructor called for Bob
\n\n

Only one constructor call! The Person object is created directly inside the vector's memory, without intermediate copies or moves.

\n\n

Performance: when is the difference critical?

\n\n

For 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

Blogs

Book Recommendations