4

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?

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
Krzysiek Karbowiak
  • 1,655
  • 1
  • 9
  • 17
  • Related and (possible duplicate): https://stackoverflow.com/q/4303513/1870232 – P0W Jan 23 '20 at 11:59
  • 3
    push and emplace are not drop-in substitutes for each other. They do two completely different things. If you fully understand the difference between the two, then you should be able to decide, yourself, which one would be more appropriate in a particular situation. This has nothing to do, whatsoever, with optimization, although the correct choice will, on occasion, have performance differences; but by itself the correct decision would be made for other reasons. – Sam Varshavchik Jan 23 '20 at 12:06
  • I always use emplace by default – JVApen Jan 23 '20 at 12:06
  • It's not like using `emplace` when `push` would do is any *worse*. You might as well get used to writing `emplace` by default. – user253751 Jan 23 '20 at 12:07
  • It's almost a duplicate, but not quite. Either way, performance isn't the deciding factor. – Bartek Banachewicz Jan 23 '20 at 12:07
  • @SamVarshavchik: I understand they are not drop-in substitutes. The issue is subtle however. You wrote a lengthy comment, but did not mention any of the reasons to choose one over the other. What those would be? My type can equally well be pushed and emplaced. So shall I push or emplace? Or doesn't matter? – Krzysiek Karbowiak Jan 23 '20 at 12:16
  • 3
    This question (and answer) explain it pretty well: https://stackoverflow.com/questions/10890653/why-would-i-ever-use-push-back-instead-of-emplace-back?noredirect=1&lq=1 In short: Use push_back if you want to call only implicit constructors. Use emplace_back otherwise. – pschill Jan 23 '20 at 12:27
  • The reasons to choose one over the other is whether you need to make a copy of an existing object into a vector or construct a brand new object in place. There's nothing "subtle" about the difference. They are two completely different things, as different as night and day. – Sam Varshavchik Jan 23 '20 at 12:50
  • @SamVarshavchik: Considering the issue of implicit and explicit constructors adds some subtlety to the problem, doesn't it? – Krzysiek Karbowiak Jan 23 '20 at 14:18

4 Answers4

4

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
} 
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
2

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.

sp2danny
  • 7,488
  • 3
  • 31
  • 53
1

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.

Andrey Semashev
  • 10,046
  • 1
  • 17
  • 27
0

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.

Benjamin Bihler
  • 1,612
  • 11
  • 32