Ефективність C ++ 11 push_back () з std :: move versus 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

Дивіться також це запитання та результати: Запитане опитування щодо VC ++ щодо вставки та розміщення
ildjarn

Відповіді:


116

Давайте подивимося, що роблять різні дзвінки, які ви надавали:

  1. emplace_back(mystring): Це побудова нового елемента на місці з будь-якими аргументами. Оскільки ви надали значення, ця побудова на місці насправді є копіювальною конструкцією, тобто це те саме, що викликатиpush_back(mystring)

  2. push_back(std::move(mystring)): Це викликає вставку переміщення, яка у випадку std :: string є конструкцією переміщення на місці.

  3. emplace_back(std::move(mystring)): Це знову конструкція на місці з наведеними Вами аргументами. Оскільки цей аргумент є значенням r, він викликає конструктор переміщення std::string, тобто це конструкція переміщення на місці, як у 2.

Іншими словами, якщо він викликається одним аргументом типу T, будь то значення rvalue або lvalue, emplace_backі push_backвони еквівалентні.

Однак для будь-якого іншого аргументу (ів) emplace_backперемагає в гонці, наприклад з a char const*in a 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, яке пов'язується лише зі значеннями r, але всередині foo, sє значенням lvalue.
Арне Мерц,

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.