Должен ли я использовать С++ 11 emplace_back с указателями?

Имея обычную базовую → Производную иерархию, например:

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

std::vector<Fruit*> m_fruits;

Имеет ли смысл (например, имеет более высокую производительность) использовать emplace_back вместо push_back?

std::vector::emplace_back( new Pear() );
std::vector::emplace_back( new Tomato() );

Ответ 1

Указатели являются скалярными типами и, следовательно, литеральными типами, поэтому копирование, перемещение и создание emplace (из lvalue или rvalue) эквивалентны и обычно скомпилируются в идентичный код (скалярная копия). push_back более понятно, что вы выполняете скалярную копию, тогда как emplace_back следует зарезервировать для конструкции emplace, вызывающей конструктор non-copy- или move-constructor (например, конструктор преобразования или multi-argument).

Если ваш вектор должен содержать std::unique_ptr<Fruit> вместо необработанных указателей (чтобы предотвратить утечку памяти), тогда, поскольку вы вызываете конструктор преобразования emplace_back, будет более правильным. Однако это может все еще протекать, если расширение вектора не удалось, поэтому в этом случае вы должны использовать push_back(make_unique<Pear>()) и т.д.

Ответ 2

Не используйте необработанные указатели, используйте std::unique_ptr следующим образом:

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

И поскольку вы не можете скопировать конструкцию std::unique_ptr, вы должны использовать emplace_back (хотя вы можете использовать push_back с std::move).


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

Edit:

Похоже, что использование std::vector<std::unique_ptr<T>>::emplace_back и new может протекать, если требуется std::vector и не удается перераспределить память, мой рекомендуемый подход (до тех пор, пока С++ 14 не вводит std::make_unique), должен использовать push_back как это:

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

Или используя std::make_unique:

m_fruits.push_back(std::make_unique<Pear>());
m_fruits.push_back(std::make_unique<Tomato>());