Difference between calling emplace_back and push_back

Question | Nov 21, 2019 | nextptr 

Background

The push_back method is used to append an element in a sequential STL container (e.g., std::vector). When inserted using the push_back, the new element is copy-or-move-constructed. The insertion could be inefficient if the passed argument to the push_back is a temporary because the temporary is constructed, copied/moved, and destroyed.

Note that there are two overloads of push_back method in a container: one that binds to a const-lvalue (which is copied) and the other binds to an rvalue (which is moved). These are some examples of insertion using push_back:

std::vector<std::string> vs;

std::string str{"Hello"};

/*'str' is lvalue. 
   It is copied to a new vector element*/
vs.push_back(str);

/*A temporary std::string is created and 
  moved to a new vector element.*/
vs.push_back("World");

We can evade the cost of building a temporary by using the emplace_back, which emplaces or directly constructs the element in-place from the given arguments. Unlike push_back that has two overloads, the emplace_back is a variadic function template. Below is an example of emplacement using emplace_back:

// std::vector<std::string> vs

//A new vector element is constructed in-place
vs.emplace_back("World");

The Question

Let's consider an arbitrary struct Foo. We have only shown those parts of Foo that are relevant here, namely a user-defined constructor, a copy-constructor, a move-constructor, and a destructor:

struct Foo {
 Foo(int i) { 
  std::cout << "ctor "; 
 }

 Foo(const Foo& f) {
  std::cout << "cpy ";
 }

 Foo(Foo&&) {
   std::cout << "mov ";
 }

 //... more code ...

 ~Foo() {
    std::cout << "dtor ";
  }
};

We append to an std::vector<Foo> using push_back and emplace_back, as shown below:

int main()
{
  std::vector<Foo> v;
  /* Reserve space for at least 2 elements
    to avoid reallocation. */
  v.reserve(2);

  //insert
  v.push_back(10);

  //emplace
  v.emplace_back(20);

  return 0;
} //vector and its elements are destroyed on exit 

When the elements are added to the container, the instances of Foo are -- created, copied/moved, and destroyed -- which would be logged on the console. Assuming that the compiler does no optimizations, what would be logged by the above code? Select the correct choice below and check Explanations for details:


Afterword

The emplace_back method can be used to construct the new element directly inside a container from the user-provided arguments, which can improve the performance because it spares the cost of building a temporary. Note that, emplace_back behaves just like push_back if the argument is of the same type as of the container's element, because there is no temporary constructed in either case. Here is an example:

std::vector<std::string> vs;
std::string s{"Hi"};

vs.emplace_back(s);
//is same as
vs.push_back(s);

For more details on that, check out Can emplace_back replace push_back?.