Эффективность push_back () C ++ 11 с std :: move по сравнению с emplace_back () для уже построенных объектов


86

В C ++ 11, emplace_back()как правило, предпочтительнее (с точки зрения эффективности), push_back()поскольку он допускает создание на месте, но так ли это при использовании push_back(std::move())с уже созданным объектом?

Например, что по- emplace_back()прежнему предпочтительнее в следующих случаях?

std::string mystring("hello world");
std::vector<std::string> myvector;

myvector.emplace_back(mystring);
myvector.push_back(std::move(mystring));
// (of course assuming we don't care about using the value of mystring after)

Кроме того, есть ли какая-то польза в приведенном выше примере вместо того, чтобы делать:

myvector.emplace_back(std::move(mystring));

Или этот шаг здесь полностью избыточен или не имеет никакого эффекта?


myvector.emplace_back(mystring);копирует и не перемещается. Два других хода должны быть эквивалентными.
— TC

Ответы:


116

Давайте посмотрим, что делают разные вызовы, которые вы предоставили:

  1. emplace_back(mystring): Это создание нового элемента на месте с любым аргументом, который вы указали. Поскольку вы указали lvalue, эта конструкция на месте фактически является копией, т.е. это то же самое, что и вызовpush_back(mystring)

  2. push_back(std::move(mystring)): Это вызывает вставку перемещения, которая в случае std :: string является конструкцией перемещения на месте.

  3. emplace_back(std::move(mystring)): Это снова внутренняя конструкция с указанными вами аргументами. Поскольку этот аргумент является rvalue, он вызывает конструктор перемещения std::string, т.е. это конструкция перемещения на месте, как в 2.

Другими словами, если вызывается с одним аргументом типа T, будь то RValue или именующее выражение, emplace_backи push_backэквивалентны.

Однако для любого другого аргумента (ов) emplace_backвыигрывает гонка, например, с a char const*в vector<string>:

  1. emplace_back("foo")требует строительства string::string(char const*)на месте.

  2. push_back("foo")сначала нужно вызвать string::string(char const*)неявное преобразование, необходимое для соответствия сигнатуре функции, а затем вставить перемещение, как в случае 2. выше. Следовательно, это эквивалентноpush_back(string("foo"))


1
Конструктор перемещения обычно более эффективен, чем конструкторы копирования, поэтому использование rvalue (случай 2, 3) более эффективно, чем использование lvalue (случай 1), несмотря на ту же семантику.
— Райан Ли

как насчет такой ситуации? void foo (строка && s) {vector.emplace (s); // 1 vector.emplace (std :: move (s)); // 2}
— VALOD9

@VALOD это снова номера 1 и 3 в моем списке. sможет быть определена как RValue ссылка , что связывается только с rvalues, но внутри foo, sэто именующий.
— Arne Mertz

1

Emplace_back получает список ссылок rvalue и пытается построить элемент контейнера прямо на месте. Вы можете вызвать emplace_back со всеми типами, которые поддерживаются конструкторами элементов контейнера. Когда вызывается emplace_back для параметров, которые не являются ссылками на rvalue, он «возвращается» к обычным ссылкам и, по крайней мере, вызывается конструктор копирования, когда параметр и элементы контейнера имеют один и тот же тип. В вашем случае myvector.emplace_back (mystring) должен сделать копию строки, потому что компилятор не мог знать, что параметр myvector подвижен. Итак, вставьте std :: move, что дает желаемое преимущество. Push_back должен работать так же, как emplace_back для уже построенных элементов.


2
Это ... интересный способ описания переадресации / универсальных ссылок.
— TC
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.