34

Having a usual Base -> Derived hierarchy, like:

class Fruit { ... };
class Pear : Fruit { ... };
class Tomato : Fruit { ... };

std::vector<Fruit*> m_fruits;

Does it make sense (e.g: is performance better) to use emplace_back instead of push_back?

std::vector::emplace_back( new Pear() );
std::vector::emplace_back( new Tomato() );
harrism
  • 26,505
  • 2
  • 57
  • 88
Zhen
  • 4,171
  • 5
  • 38
  • 57

2 Answers2

35

Don't use raw pointers, use std::unique_ptr like this:

std::vector<std::unique_ptr<Fruit>> m_fruits;

And as you can't copy construct a std::unique_ptr you must use emplace_back (although you can use push_back with std::move).

m_fruits.emplace_back(new Pear());
m_fruits.emplace_back(new Tomato());

Edit:

As it appears that using std::vector<std::unique_ptr<T>>::emplace_back and new can leak if the std::vector needs and fails to reallocate memory, my recommended approach (until C++14 introduces std::make_unique) is to use push_back like this:

m_fruits.push_back(std::unique_ptr<Fruit>(new Pear()));
m_fruits.push_back(std::unique_ptr<Fruit>(new Tomato()));

Or using std::make_unique:

m_fruits.push_back(std::make_unique<Pear>());
m_fruits.push_back(std::make_unique<Tomato>());
Felix Glas
  • 15,065
  • 7
  • 53
  • 82
  • Is the method in the Edit still be true in c++17 and c++20? – r0n9 Jul 30 '19 at 04:24
  • 1
    @r0ng Yes, as far as I know `vec.emplace_back(new Object())` can still leak in C++17 and C++2a if the vector fails to allocate memory for the new element. Use the suggestions in the edit. – Felix Glas Jul 31 '19 at 10:12
  • Is there any drawback to use emplace_back with make_unique? like `m_fruits.emplace_back (std::make_unique());` @FelixGlas – Defisher Jan 20 '22 at 21:41
  • 1
    @Defisher In practice, no. But it's a little bit misusing the api as `emplace_back` is meant for in-place construction and is taking the ctor arguments for the element type. E.g. if `Fruit` were a concrete class taking a parameter `const std::string& name`, then you would call `m_fruits.emplace_back("Pear")` etc. Back to the example - calling `m_fruits.emplace_back(std::make_unique())` will behind the scenes pass on the argument to the in-place construction of the element `std::unique_ptr(arg)`, i.e. (unnecessarily) using the copy/move ctor of the element type. – Felix Glas Jan 25 '22 at 09:01
27

Pointers are scalar types and therefore literal types, and so copy, move and emplace construction (from an lvalue or rvalue) are all equivalent and will usually compile to identical code (a scalar copy). push_back is clearer that you're performing a scalar copy, whereas emplace_back should be reserved for emplace construction calling a non-copy- or move- constructor (e.g. a converting or multi-argument constructor).

If your vector should hold std::unique_ptr<Fruit> instead of raw pointers (to prevent memory leaks) then because you're calling a converting constructor emplace_back would be more correct. However that can still leak if extending the vector fails, so in that case you should use push_back(make_unique<Pear>()) etc.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • make_unique isn't in the standar, and it appears that emplace_back is same that push_back( std::move( X ) ) [i'm using gcc 4.7] – Zhen Apr 03 '13 at 10:51
  • 6
    @Zhen: It may appear that way, but it's not. `emplace_back(new T())` can *leak*, while `push_back(make_unique())` cannot. And yes, `make_unique` is missing by oversight, you can find implementations around the web. – GManNickG Apr 03 '13 at 16:38