Push_back vs 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
?
Ответы
Ответ 1
В дополнение к тому, что посетитель сказал:
Функция 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>()
, tuple и новые вещи в <functional>
. Это препроцессорное оборудование относительно сложно в использовании и обслуживании. Кроме того, это существенно влияет на скорость компиляции, так как нам приходится многократно включать подзаголовки. Из-за сочетания наших временных ограничений и проблем со скоростью компиляции мы не смоделировали шаблоны переменных в наших функциях emplace.
Когда в компиляторе реализованы различные шаблоны, можно ожидать, что мы воспользуемся ими в библиотеках, в том числе в наших функциях emplace. Мы очень серьезно относимся к соответствию, но, к сожалению, мы не можем сделать все сразу.
Это понятное решение. Каждый, кто хотя бы один раз пытался эмулировать шаблон вариации с ужасными приемами препроцессора, знает, как отвратительно получается это.
Ответ 2
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
Ответ 3
Оптимизация для 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 &&)
Ответ 4
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
эффективно с отображенным типом, который не является ни копируемым, ни подвижным.
Ответ 5
Еще один в случае списков:
//создает элементы на месте.
emplace_back ( "элемент");
//Он создаст новый объект и затем скопирует (или переместит) его значение аргументов. push_back (explicitDataType { "элемент"});
Ответ 6
Хороший код для push_back и emplace_back показан здесь.
http://en.cppreference.com/w/cpp/container/vector/emplace_back
Вы можете увидеть операцию перемещения на push_back, а не на emplace_back.
Ответ 7
Вот хорошая ссылка на разницу
Вставить v. Emplace