push_back против emplace_back - PullRequest
       58

push_back против emplace_back

635 голосов
/ 29 ноября 2010

Я немного смущен разницей между push_back и emplace_back.

void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);

Поскольку существует перегрузка push_back с использованием ссылки на rvalue, я не совсем понимаю, для чего предназначена цель emplace_back?

Ответы [ 6 ]

499 голосов
/ 29 ноября 2010

В дополнение к тому, что посетитель сказал:

Функция void emplace_back(Type&& _Val), предоставляемая MSCV10, не соответствует и избыточна, потому что, как вы заметили, она строго эквивалентна push_back(Type&& _Val).

Нореальная форма C ++ 0x emplace_back действительно полезна: void emplace_back(Args&&...);

Вместо того, чтобы принимать value_type, требуется список аргументов с переменным числом аргументов, что означает, чтоТеперь вы можете полностью пересылать аргументы и напрямую создавать объект в контейнере без какого-либо временного объекта.

Это полезно, потому что независимо от того, насколько умный RVO и перемещение семантики привносят в таблицу, все еще есть сложные случаи, когда push_back может создавать ненужные копии (или перемещаться).Например, с традиционной insert() функцией std::map вам необходимо создать временный объект, который затем будет скопирован в std::pair<Key, Value>, который затем будет скопирован на карту:

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);

Так почему же они не реализовали правильную версию emplace_back в MSVC?На самом деле, это слишком долго меня беспокоило, поэтому я задал тот же вопрос в блоге Visual C ++ .Вот ответ от Стефана Т Лававея, официального сопровождающего реализации стандартной библиотеки Visual C ++ в Microsoft.

В: Являются ли бета 2-функции emplace просто своего рода заполнителем прямо сейчас?

A: Как вы, возможно, знаете, шаблоны Variad не реализованы в VC10.Мы моделируем их с помощью препроцессорного оборудования для таких вещей, как make_shared<T>(), кортеж и новые вещи в <functional>.Это препроцессорное оборудование относительно сложно в использовании и обслуживании.Кроме того, это существенно влияет на скорость компиляции, так как нам приходится многократно включать подзаголовки.Из-за сочетания наших ограничений по времени и проблем со скоростью компиляции мы не смоделировали шаблоны переменных в наших функциях emplace.

Когда шаблоны переменных реализованы в компиляторе, можно ожидать, что мы воспользуемся преимуществомих в библиотеках, в том числе в наших функциях emplace.Мы очень серьезно относимся к соответствию, но, к сожалению, мы не можем сделать все сразу.

Это понятное решение.Каждый, кто хотя бы один раз пытался эмулировать шаблон вариации с ужасными приемами препроцессора, знает, как отвратительно получается это.

182 голосов
/ 29 ноября 2010

emplace_back не должен принимать аргумент типа vector::value_type, но вместо этого использовать переменные аргументы, которые передаются в конструктор добавляемого элемента.

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

Можно передать value_type, который будет перенаправлен в конструктор копирования.

Поскольку он перенаправляет аргументы, это означает, что если у вас нет значения rvalue, это все равно означает, что контейнер будет хранить «скопированную» копию, а не перемещенную копию.

 std::vector<std::string> vec;
 vec.emplace_back(std::string("Hello")); // moves
 std::string s;
 vec.emplace_back(s); //copies

Но вышеупомянутое должно быть идентично тому, что делает push_back. Скорее всего, он предназначен для случаев использования, таких как:

 std::vector<std::pair<std::string, std::string> > vec;
 vec.emplace_back(std::string("Hello"), std::string("world")); 
 // should end up invoking this constructor:
 //template<class U, class V> pair(U&& x, V&& y);
 //without making any copies of the strings
60 голосов
/ 27 октября 2015

Оптимизация для emplace_back может быть продемонстрирована в следующем примере.

Для emplace_back будет вызван конструктор A (int x_arg). И для push_back A (int x_arg) вызывается первым, а move A (A &&rhs) вызывается впоследствии.

Конечно, конструктор должен быть помечен как explicit, но для текущего примера хорошо бы удалить явность.

#include <iostream>
#include <vector>
class A
{
public:
  A (int x_arg) : x (x_arg) { std::cout << "A (x_arg)\n"; }
  A () { x = 0; std::cout << "A ()\n"; }
  A (const A &rhs) noexcept { x = rhs.x; std::cout << "A (A &)\n"; }
  A (A &&rhs) noexcept { x = rhs.x; std::cout << "A (A &&)\n"; }

private:
  int x;
};

int main ()
{
  {
    std::vector<A> a;
    std::cout << "call emplace_back:\n";
    a.emplace_back (0);
  }
  {
    std::vector<A> a;
    std::cout << "call push_back:\n";
    a.push_back (1);
  }
  return 0;
}

выход:

call emplace_back:
A (x_arg)

call push_back:
A (x_arg)
A (A &&)
8 голосов
/ 24 сентября 2013

emplace_back соответствующая реализация будет перенаправлять аргументы в конструктор vector<Object>::value_type при добавлении в вектор.Напоминаю, что Visual Studio не поддерживала шаблоны с переменными значениями, но с помощью шаблонов с переменными параметрами будет поддерживаться в Visual Studio 2013 RC, поэтому я предполагаю, что будет добавлена ​​соответствующая подпись.Прямо к конструктору vector<Object>::value_type, строго говоря, вам не нужно, чтобы тип был перемещаемым или копируемым для функции emplace_back.В случае vector<NonCopyableNonMovableObject> это бесполезно, так как vector<Object>::value_type для копирования требуется копируемый или подвижный тип.

Но обратите внимание , что это может быть полезно для std::map<Key, NonCopyableNonMovableObject>, поскольку после размещения записи на карте ее больше не нужно перемещать или копировать, в отличие от vector, что означает, что вы можете эффективно использовать std::map с сопоставленным типом, который нельзя ни копировать, ни перемещать.

5 голосов
/ 27 февраля 2018

Здесь показан хороший код для push_back и emplace_back.

http://en.cppreference.com/w/cpp/container/vector/emplace_back

Вы можете видеть операцию перемещения на push_back, а не на emplace_back.

5 голосов
/ 28 декабря 2017

Еще один случай в случае списков:

// создает элементы на месте.
emplace_back ("element");

// Создает новый объект и затем копирует(или переместить) значение аргументов.push_back (explicitDataType { "элемент"});

...