Skip to content
C++ Better Explained
Go back
push_back vs emplace_back in C++: What's the Real Difference?

push_back vs emplace_back in C++: What’s the Real Difference?

Every C++ beginner hits this moment: you’ve been using push_back for months, someone reviews your code and says “use emplace_back, it’s faster,” and you’re left wondering whether you’ve been doing it wrong the whole time.

You haven’t. But the difference is real, and it’s worth understanding exactly — because “always use emplace_back” is not the right takeaway.


The One-Sentence Version

push_back takes an object. emplace_back takes the arguments to build an object.

std::vector<std::string> names;

names.push_back(std::string("Sahil"));  // build a string, then hand it over
names.emplace_back("Sahil");            // hand over the argument, build in place

The first line creates a temporary std::string, then moves it into the vector, then destroys the temporary. The second line constructs the string directly in the vector’s memory. One fewer object gets created and destroyed.


Seeing It Happen

Talking about copies is abstract. Let’s make a class that announces every time it’s constructed, copied, or moved:

#include <iostream>
#include <vector>
#include <string>

class Player {
public:
    Player(std::string name, int score)
        : name_(std::move(name)), score_(score) {
        std::cout << "  constructed " << name_ << "\n";
    }

    Player(const Player& other)
        : name_(other.name_), score_(other.score_) {
        std::cout << "  COPIED " << name_ << "\n";
    }

    Player(Player&& other) noexcept
        : name_(std::move(other.name_)), score_(other.score_) {
        std::cout << "  MOVED " << name_ << "\n";
    }

private:
    std::string name_;
    int score_;
};

int main() {
    std::vector<Player> players;
    players.reserve(4);  // avoid reallocation muddying the output

    std::cout << "push_back:\n";
    players.push_back(Player("Alice", 10));

    std::cout << "emplace_back:\n";
    players.emplace_back("Bob", 20);

    return 0;
}

Output:

push_back:
  constructed Alice
  MOVED Alice
emplace_back:
  constructed Bob

There it is. push_back built a Player on the stack and then moved it into the vector — two operations. emplace_back built it once, in its final home.

Notice the reserve(4) call. Without it, a vector that runs out of capacity moves all existing elements to new storage, which would flood the output with unrelated moves. See reserve vs resize for why that matters for performance too.

If you're looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you're a complete beginner or looking to solidify your understanding. Just $19.

When emplace_back Saves You Nothing

Here’s the part the “always use emplace_back” advice gets wrong. Change the calls to pass an existing object:

Player existing("Carol", 30);

players.push_back(existing);     // COPIED Carol
players.emplace_back(existing);  // COPIED Carol

Identical. emplace_back forwards whatever you give it to a constructor — and when you give it a Player, the constructor it calls is the copy constructor. There is no magic that avoids a copy of an object that already exists.

The same applies to int, double, and other trivial types:

std::vector<int> numbers;
numbers.push_back(42);     // copies 4 bytes
numbers.emplace_back(42);  // copies 4 bytes

Nothing is saved because there was nothing to save. If someone tells you switching a vector<int> from push_back to emplace_back is a performance win, they’re mistaken.


The Trap: emplace_back Accepts Too Much

push_back will refuse a conversion marked explicit. emplace_back calls the constructor directly, so it happily uses explicit constructors:

#include <vector>
#include <fstream>

std::vector<std::ofstream> files;

// files.push_back("log.txt");     // does NOT compile — explicit constructor
files.emplace_back("log.txt");     // compiles, opens a file

That’s convenient when you mean it and a silent bug when you don’t. A classic version of the problem:

std::vector<std::vector<int>> grid;

grid.emplace_back(10);  // a vector of 10 zeros — probably not what you wanted

You may have meant “add a vector containing the number 10.” You got a vector of ten zeros, because std::vector’s size constructor matched your argument. push_back would have rejected this outright.


The Practical Rule

SituationUse
You already have an objectpush_back
You have a std::moved objectpush_back
Building from constructor argumentsemplace_back
vector<int>, vector<double>, etc.Either — no difference

The rule that holds up: use emplace_back when you’re constructing, push_back when you’re inserting. That reads clearly to whoever maintains the code next, and it happens to be the fast choice in both cases.

And if the object you’re inserting is expensive and you don’t need it afterwards, push_back(std::move(obj)) gets you a move instead of a copy — see move semantics for how that works.



Take Your C++ Further

If you’re looking to go deeper with C++, the C++ Better Explained Ebook is perfect for you — whether you’re a complete beginner or looking to solidify your understanding. Just $19.

👉 Get the C++ Better Explained Ebook — $19

📋

Free Download: The 10 Mistakes Every C++ Beginner Makes

A free 1-page checklist that shows the exact traps that slow down every C++ beginner — so you can avoid them from day one.

🔒 No spam. Unsubscribe anytime.


Share this post on:

Written by

Sahil Bora

Software Engineer. Author and creator of C++ Better Explained.


Previous Post
C++ Program to Count Vowels in a String (3 Ways)
Next Post
INT_MAX and numeric_limits in C++: Finding a Type's Range

Keep Learning