Copy constructor and emplace_back() in C++
A copy constructor of class T is a non-template constructor whose first parameter is
volatile T&, or
const volatile T&, and either there are no other parameters, or the rest of the parameters all have default values.
// Typical declaration of a copy constructor.
In C++, a Copy Constructor may be called in following cases:
- When an object of the class is returned by value.
- When an object of the class is passed (to a function) by value as an argument.
- When an object is constructed based on another object of the same class.
- When compiler generates a temporary object.
Let’s talk about some cases where you might not even realize how many copies it has done. Copy is not cheap, sometimes. In some cases, copy constructor can be avoided by Copy Elision, but it is not in the scope of this discussion.
How many times the copy constructor is called?
The answer is
0, it creates a new Point object, calls normal constructor.
- Pushes it into vector, copy once.
1, it creates a new Point object again, calls normal constructor.
- Pushes again, copy again.
- Wait, before push, vector
pointsneeds to resize (usually double the size), copy
pointsto new vector first, then push.
- In total,
3copy constructor calls and
=== 0 ===
You can run this code online, https://onlinegdb.com/BkuZ6vFB7. Play with it, change
n to some other value to see if you can get the right number of copy constructor calls.
Yes, we can use
std::vector::emplace_back() instead of
(It) Appends a new element to the end of the container. The element is constructed through
std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container.
Let’s change the
How many times does copy constructor get called? Only once.
emplace_backcalls the normal constructor of
Point, put the newly created object in vector directly. no copy.
pointsresizes itself, need to copy the existing element
points, one copy.
- Then, as the first step,
emplace_backcreates new object in place, no copy needed.
=== 0 ===
You can also run the code online, https://onlinegdb.com/SJo_lOKSX. Change
n to some other values to see if you can get the right number of copy constructor calls.
We can not deny the fact that
std::vector::emplace_back() saves unnecessary copy operations and it can be a big win for some operations on large objects. However, using
emplace_back means you need to take care of the constructor arguments, which sometimes could be arbitrary. Check the example in Tip of the Week #112: emplace vs. push_back, the second example constructs a vector of over a million elements, allocating several megabytes of memory in the process, if the variable type is not as what you think, and this is dangerous.
For more expensive types, this may be a reason to use
push_back(), despite the readability and safety costs, but then again it may not. Very often the performance difference just won’t matter. As always, the rule of thumb is that you should avoid optimizations that make the code less safe or less clear, unless the performance benefit is big enough to show up in your application benchmarks.
Let’s keep this in mind.