Push_Back VS Emplace_Back

push_back vs emplace_back

In addition to what visitor said :

The function void emplace_back(Type&& _Val) provided by MSCV10 is non conforming and redundant, because as you noted it is strictly equivalent to push_back(Type&& _Val).

But the real C++0x form of emplace_back is really useful: void emplace_back(Args&&...);

Instead of taking a value_type it takes a variadic list of arguments, so that means that you can now perfectly forward the arguments and construct directly an object into a container without a temporary at all.

That's useful because no matter how much cleverness RVO and move semantic bring to the table there is still complicated cases where a push_back is likely to make unnecessary copies (or move). For example, with the traditional insert() function of a std::map, you have to create a temporary, which will then be copied into a std::pair<Key, Value>, which will then be copied into the map :

std::map<int, Complicated> m;
int anInt = 4;
double aDouble = 5.0;
std::string aString = "C++";

// cross your finger so that the optimizer is really good
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString)));

// should be easier for the optimizer
m.emplace(4, anInt, aDouble, aString);

So why didn't they implement the right version of emplace_back in MSVC? Actually, it bugged me too a while ago, so I asked the same question on the Visual C++ blog. Here is the answer from Stephan T Lavavej, the official maintainer of the Visual C++ standard library implementation at Microsoft.

Q: Are beta 2 emplace functions just some kind of placeholder right now?

A: As you may know, variadic templates
aren't implemented in VC10. We
simulate them with preprocessor
machinery for things like
make_shared<T>(), tuple, and the new
things in <functional>. This
preprocessor machinery is relatively
difficult to use and maintain. Also,
it significantly affects compilation
speed, as we have to repeatedly
include subheaders. Due to a
combination of our time constraints
and compilation speed concerns, we
haven't simulated variadic templates
in our emplace functions.

When variadic templates are
implemented in the compiler, you can
expect that we'll take advantage of
them in the libraries, including in
our emplace functions. We take
conformance very seriously, but
unfortunately, we can't do everything
all at once.

It's an understandable decision. Everyone who tried just once to emulate variadic template with preprocessor horrible tricks knows how disgusting this stuff gets.

push_back vs emplace_back to a std::vectorstd::string

I take a while to really understand what the advantage to use std::vector::emplace as aschepler said.

I found out the better scenario to use that is when we have our own class that receive some data when it is construct.

To make more clear, let's suppose we have:

  1. A vector of MyObject
  2. MyObject needs to receive 3 arguments to be constructed
  3. The functions get1stElem(), get2ndElem() and get3rdElem() provide the elements necessary to construct a MyObject instance

Then we can have a line like this:

vVec.emplace(get1stElem(), get2ndElem(), get3rdElem());

Then the std::vector::emplace will construct MyObject in place more efficiently than std::vector::push_back.

Why would I ever use push_back instead of emplace_back?

I have thought about this question quite a bit over the past four years. I have come to the conclusion that most explanations about push_back vs. emplace_back miss the full picture.

Last year, I gave a presentation at C++Now on Type Deduction in C++14. I start talking about push_back vs. emplace_back at 13:49, but there is useful information that provides some supporting evidence prior to that.

The real primary difference has to do with implicit vs. explicit constructors. Consider the case where we have a single argument that we want to pass to push_back or emplace_back.

std::vector<T> v;
v.push_back(x);
v.emplace_back(x);

After your optimizing compiler gets its hands on this, there is no difference between these two statements in terms of generated code. The traditional wisdom is that push_back will construct a temporary object, which will then get moved into v whereas emplace_back will forward the argument along and construct it directly in place with no copies or moves. This may be true based on the code as written in standard libraries, but it makes the mistaken assumption that the optimizing compiler's job is to generate the code you wrote. The optimizing compiler's job is actually to generate the code you would have written if you were an expert on platform-specific optimizations and did not care about maintainability, just performance.

The actual difference between these two statements is that the more powerful emplace_back will call any type of constructor out there, whereas the more cautious push_back will call only constructors that are implicit. Implicit constructors are supposed to be safe. If you can implicitly construct a U from a T, you are saying that U can hold all of the information in T with no loss. It is safe in pretty much any situation to pass a T and no one will mind if you make it a U instead. A good example of an implicit constructor is the conversion from std::uint32_t to std::uint64_t. A bad example of an implicit conversion is double to std::uint8_t.

We want to be cautious in our programming. We do not want to use powerful features because the more powerful the feature, the easier it is to accidentally do something incorrect or unexpected. If you intend to call explicit constructors, then you need the power of emplace_back. If you want to call only implicit constructors, stick with the safety of push_back.

An example

std::vector<std::unique_ptr<T>> v;
T a;
v.emplace_back(std::addressof(a)); // compiles
v.push_back(std::addressof(a)); // fails to compile

std::unique_ptr<T> has an explicit constructor from T *. Because emplace_back can call explicit constructors, passing a non-owning pointer compiles just fine. However, when v goes out of scope, the destructor will attempt to call delete on that pointer, which was not allocated by new because it is just a stack object. This leads to undefined behavior.

This is not just invented code. This was a real production bug I encountered. The code was std::vector<T *>, but it owned the contents. As part of the migration to C++11, I correctly changed T * to std::unique_ptr<T> to indicate that the vector owned its memory. However, I was basing these changes off my understanding in 2012, during which I thought "emplace_back does everything push_back can do and more, so why would I ever use push_back?", so I also changed the push_back to emplace_back.

Had I instead left the code as using the safer push_back, I would have instantly caught this long-standing bug and it would have been viewed as a success of upgrading to C++11. Instead, I masked the bug and didn't find it until months later.

emplace_back() vs push_back() for vector

emplace_back constructs the element in-place by forwarding the arguments to the constructor of element type, so you can

v.emplace_back(1); // forwarding 1 to Int::Int(int) to construct the element directly

push_back always expects an element, i.e. an Int. When you pass 1 to it as v.push_back(1);, implicit conversion happens. A temporary Int is constructed from 1 by (Int::Int(int) then passed to push_back, the element is constructed from the temporary by the move constructor of Int later. I.e. one more move constructor invocation than v.emplace_back(1);.

You can also pass an Int to emplace_back like v.emplace_back(Int(1));, as explained above, the temporary Int is forwarded to Int's move constructor to construct the element, which does the same thing as v.push_back(Int(1));.

As @JeJo suggested, there's another difference between emplace_back and push_back since C++17. emplace_back returns a reference to the inserted element while push_back returns nothing.

C++ Emplace Back vs Push Back Vector

emplace_back passes on the arguments given to it to the constructor of the element type to construct it in-place. This is what allows emplace_back to be used without creating a temporary object.

You are not making use of that. You are still creating a temporary in v.emplace_back(Foo(34)); with the expression Foo(34). A reference to this Foo(34) temporary is then passed to the (copy) constructor of Foo to create the element in-place, but the temporary is already created outside the emplace_back call.

Instead, pass on just the arguments to the constructor:

v.emplace_back(34);

Which will pass 34 to the constructor of Foo to construct it in-place without any temporary copy.

push_back is more efficient than emplace_back?

push_back is not more efficient, and the results you observe are due to the vector resizing itself.

When you call emplace after push_back, the vector has to resize itself to make room for the second element. This means that it has to move the A that was originally inside the vector, making emplace appear more complex.

If you reserve enough space in the vector beforehand, this doesn't happen. Notice the call to va.reserve(2) after va's creation:

#include <iostream>     
#include <vector>

class A
{
public:
A() {std::cout << "A const" << std::endl;}
~A() {std::cout << "A dest" << std::endl;}
A(const A& a) {std::cout << "A copy const" << std::endl;}
A(A&& a) {std::cout << "A move const" << std::endl;}
A& operator=(const A& a) {std::cout << "A copy operator=" << std::endl; return *this; }
A& operator=(A&& a) {std::cout << "A move operator=" << std::endl; return *this; }
};

int main () {
std::vector<A> va;
// Now there's enough room for two elements
va.reserve(2);
std::cout <<"push:" << std::endl;
va.push_back(A());
std::cout <<std::endl<< "emplace:" << std::endl;
va.emplace_back(A());

std::cout <<std::endl<< "end:" << std::endl;

return 0;
}

The corresponding output is:

push:
A const
A move const
A dest

emplace:
A const
A move const
A dest

end:
A dest
A dest

Can we make things even more efficient? Yes! emplace_back takes whatever arguments you provide it, and forwards them to A's constructor. Because A has a constructor that takes no arguments, you can also use emplace_back with no arguments! In other words, we change

va.emplace_back(A());

to

va.emplace_back(); // No arguments necessary since A is default-constructed

This results in no copy, and no move:

push:
A const
A move const
A dest

emplace:
A const

end:
A dest
A dest

A note on vectors resizing: It's important to note that the implementation of std::vector is smart. If A had been a trivially copyable type, std::vector might have been able resize in-place without additional copying using a system function similar to realloc. However because As constructors and destruction contain code, realloc can't be used here.

Why emplace_back is faster than push_back?

Your test case isn't very helpful. push_back takes a container element and copies/moves it into the container. emplace_back takes arbitrary arguments and constructs from those a new container element. But if you pass a single argument that's already of element type to emplace_back, you'll just use the copy/move constructor anyway.

Here's a better comparison:

Foo x; Bar y; Zip z;

v.push_back(T(x, y, z)); // make temporary, push it back
v.emplace_back(x, y, z); // no temporary, directly construct T(x, y, z) in place

The key difference, however, is that emplace_back performs explicit conversions:

std::vector<std::unique_ptr<Foo>> v;
v.emplace_back(new Foo(1, 'x', true)); // constructor is explicit!

This example will be mildly contrived in the future, when you should say v.push_back(std::make_unique<Foo>(1, 'x', true)). However, other constructions are very nice with emplace, too:

std::vector<std::thread> threads;
threads.emplace_back(do_work, 10, "foo"); // call do_work(10, "foo")
threads.emplace_back(&Foo::g, x, 20, false); // call x.g(20, false)

Using make_shared with emplace_back and push_back - any difference?

vector<T>::push_back has a T&& overload, which does the same as the vector<T>::emplace_back T&& version.

The difference is that emplace_back will perfect-forward any set of arguments to the T's constructor, while push_back only takes T&& or T const&. When you actually pass a T&& or T const& the standard specification of their behaviour is the same.

emplace_back() vs push_back when inserting a pair into std::vector

emplace_back takes a variadic parameter pack as argument:

template< class... Args >
reference emplace_back( Args&&... args );

When you call it like this: emplace_back({1, 2}) you are calling it with one argument i.e. {1, 2} and the Args cannot be deduced. That is because of how the language has evolved. In C++ {1, 2} doesn't have a type. It is a brace enclosed init-list and can be used in certain types of initializations, but all require the type of the initialized to be known. That is why temp_pair = {1,2}; works, because the type of temp_pair is know and has a constructor matching (int, int).

Anyway emplace_back wasn't supposed to be used like that, but like this instead:

my_vec.emplace_back(1, 2);

Also please note that even if these work:

my_vec.emplace_back(std::pair<int, int>{1, 2});
my_vec.emplace_back(temp_pair);

They shouldn't be used. They add no advantage over push_back. The whole point of emplace_back is to avoid creating a temporary T. The above calls all create the temporary std::pair<int, int>.


but I thought you can use emplace_back() anywhere that you have
push_back()

For the most part that's correct. At least that was the intention. And you can indeed use it in your cese. You just have to adjust the syntax a little. So instead of push_back({1, 2}) you can use emplace_back(1, 2).

There is a situation where unfortunately you can't use emplace_back: aggregates.

struct Agg
{
int a, b;
};

auto test()
{
Agg a{1, 2}; // ok, aggregate initialization

std::vector<Agg> v;
v.emplace_back(1, 2); // doesn't work :(
}

This doesn't work unless you add a constructor for Agg. This is considered an open defect in the standard, but unfortunately they can't find a good solution to this. The problem is with how brace init initialization works and if you use it in generic code you can miss some constructors. For all the nitty-gritty details check this great post: Why can an aggreggate struct be brace-initialized, but not emplaced using the same list of arguments as in the brace initialization?

Efficiency of C++11 push_back() with std::move versus emplace_back() for already constructed objects

Let's see what the different calls that you provided do:

  1. emplace_back(mystring): This is an in-place construction of the new element with whatever argument you provided. Since you provided an lvalue, that in-place construction in fact is a copy-construction, i.e. this is the same as calling push_back(mystring)

  2. push_back(std::move(mystring)): This calls the move-insertion, which in the case of std::string is an in-place move-construction.

  3. emplace_back(std::move(mystring)): This is again an in-place construction with the arguments you provided. Since that argument is an rvalue, it calls the move-constructor of std::string, i.e. it is an in-place move-construction like in 2.

In other words, if called with one argument of type T, be it an rvalue or lvalue, emplace_back and push_back are equivalent.

However, for any other argument(s), emplace_back wins the race, for example with a char const* in a vector<string>:


  1. emplace_back("foo") calls std::string(char const*) for in-place-construction.

  2. push_back("foo") first has to call std::string(char const*) for the implicit conversion needed to match the function's signature, and then a move-insertion like case 2. above. Therefore it is equivalent to push_back(string("foo"))



Related Topics



Leave a reply



Submit