How to choose between `push_*()` and `emplace_*()` functions?

328 Views Asked by At

I understand the difference between the two function variants.

My question is: should I normally use good old push_*() version and only switch to emplace_*() when my profiler tells me this will benefit performance (that is, do not optimise prematurely)? Or should I switch to using emplace_*() as the default (perhaps not to pessimise the code unnecessarily - similar to i++ vs ++i in for loops)?

Is any of the variants more universal than the other (that is, imposes less constraints on the type being inserted) in realistic non-contrived use cases?

4

There are 4 best solutions below

2
On BEST ANSWER

While writing the code I would not worry about performance. Performance is for later when you already have code that you can profile.

I'd rather worry about expressiveness of the code. Roughly speaking, push_back is for when you have an element and want to place a copy inside the container. emplace_back is to construct the element in place.

Consider what has the lower "wtf-count":

struct foo {int x;int y;};

void foo_add(const foo& f,std::vector<foo>& v) {
    v.emplace_back(f);   // wtf ?!? we already have a foo
    v.push_back(f);      // ... simply make a copy (or move)
}

void foo_add(int x, int y, std::vector<foo>& v) {
    auto z = foo{x,y};      // wtf ?!? 
    f.push_back(z);         // why create a temporary?
    f.emplace_back(x,y);    // ... simply construct it in place
} 
0
On

If you switch from push_back to emplace_back in a naive way, you will have no advantage at all. Consider the following code:

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

struct President
{
    std::string name;
    std::string country;
    int year;

    President(std::string p_name, std::string p_country, int p_year) :
            name(std::move(p_name)), country(std::move(p_country)), year(p_year)
    {
        std::cout << "I am being constructed.\n";
    }
    President(President&& other) :
            name(std::move(other.name)), country(std::move(other.country)),
            year(other.year)
    {
        std::cout << "I am being moved.\n";
    }
    President& operator=(const President& other) = default;
};

int main()
{
    std::vector<President> elections;
    std::cout << "emplace_back:\n";
    elections.emplace_back("Nelson Mandela", "South Africa", 1994);

    std::vector<President> reElections;
    std::cout << "\npush_back:\n";
    reElections.push_back(
            President("Franklin Delano Roosevelt", "the USA", 1936));

    std::cout << "\nContents:\n";

    for (President const& president : elections)
    {
        std::cout << president.name << " was elected president of "
                            << president.country << " in " << president.year << ".\n";
    }

    for (President const& president : reElections)
    {
        std::cout << president.name << " was re-elected president of "
                            << president.country << " in " << president.year << ".\n";
    }
}

If you replace push_back by emplace_back you still have a construction and then a move. Only if you pass the arguments needed for construction instead of the constructed instance itself (see the call to emplace_back), you have saved effort.

0
On

emplace functions are delegating constructors.

Let's say you have a container of T.

If you already have a T, maybe it's const, maybe it's a rvalue, maybe none of those;
Then you use push_xxx().
Your object will be copied/moved into the container.

If you instead want to construct a T, then you use emplace_xxx(), with the same parameters you would send the constructor.
An object will be constructed directly in the container.

0
On

Emplace functions are more generic than push functions. In no case they are less efficient, on the contrary - they can be more efficient, as they allow to optimize away one copy/move operation of the container element when you need to construct it from arguments. When putting an element into container involves copy/move anyway, emplace and push operations are equivalent.

Push can be preferable, if you actually want to enforce construction before copying/moving the element into the container. For example, if your element type has some special logic in its constructor that you want to execute before the container is modified. Such cases are quite rare, though.