push_back против emplace_back

Я немного сбит с толку относительно разницы между 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 ответов

Решение

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

Функция 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. Мы очень серьезно относимся к соответствию, но, к сожалению, мы не можем сделать все сразу.

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

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

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

За emplace_back конструктор A (int x_arg) будет называться. И для push_backA (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 &&)

Конкретный вариант использования для emplace_back: Если вам нужно создать временный объект, который затем будет помещен в контейнер, используйте emplace_back вместо того push_back. Он создаст объект на месте внутри контейнера.

Заметки:

  1. push_backв приведенном выше случае создаст временный объект и переместит его в контейнер. Однако конструкция на месте, используемая дляemplace_back будет более производительным, чем создание и последующее перемещение объекта (что обычно требует некоторого копирования).
  2. В общем, вы можете использовать emplace_back вместо того push_backво всех случаях без особых проблем. (См. Исключения)

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

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

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

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

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

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

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

С emplace_back, если вы направите аргументы непосредственно vector<Object>::value_type конструктор, вам не нужен тип, чтобы быть подвижным или копируемым для emplace_back функционировать, строго говоря. в vector<NonCopyableNonMovableObject> случай, это не полезно, так как vector<Object>::value_type для роста нужен копируемый или подвижный тип.

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

Другие вопросы по тегам