Std::vector:: emplace_back и std:: move

Есть ли какое-либо преимущество при использовании std::vector::emplace_back и std::move вместе? или он просто лишний, так как std::vector::emplace_back выполнит inplace-construction?

Случаи для разъяснения:

std::vector<std::string> bar;

Во-первых:

bar.emplace_back(std::move(std::string("some_string")));

Второе:

std::string str("some_string");
bar.emplace_back(std::move(str));

Третье:

bar.emplace_back(std::move("some_string"));

Ответ 1

Во второй версии есть преимущество. Вызов emplace_back вызовет конструктор перемещения std::string, когда используется std::move, который может быть сохранен на копии (пока эта строка не будет сохранена в буфере SSO). Обратите внимание, что в этом случае это по существу то же самое, что и push_back.

std::move в первой версии не требуется, так как строка уже является prvalue.

std::move в третьей версии не имеет значения, поскольку строковый литерал нельзя перенести из.

Самый простой и эффективный метод:

bar.emplace_back("some_string");

Это не требует ненужных конструкций std::string, поскольку литерал идеально пересылается конструктору.

Ответ 2

emplace_back вызывает что-то вроде

new (data+size) T(std::forward<Args>(args)...);

если args являются базовыми - не-rvalue-referenced std::string, выражение будет скомпилировано с

new (data+size) std::string(str); //str is lvalue - calls std::string::string(const string& rhs)

означает, что будет создан конструктор копирования. но, если вы используете std::move on str, код будет скомпилирован для

new (data+size) std::string(str); //str is r-value reference, calls std::string::string(string&& rhs)

поэтому происходит семантика перемещения. это огромный прирост производительности.
обратите внимание, что str является lvalue, у него есть имя, поэтому для создания r-value-reference из него вы должны использовать std::move.

в примере

vec.emplace_back("some literal"); 

код будет скомпилирован для

new (data+size) std::string("literal"); //calls std::string::string(const char*);

поэтому нет временных.

третий пример - глупость. вы не можете перемещать литералы.

Ответ 3

Вся идея emplace_back состоит в том, чтобы избавиться от операций копирования и перемещения. Вам просто нужно передать входные параметры std::string в emplace_back. Объект std::string будет построен внутри метода emplace_back.

bar.emplace_back("some_string");

Если у вас уже есть строка, имеет смысл использовать std::move. Объект std::string будет построен внутри emplace_back, перемещая данные из str.

std::string str("some_string");
bar.emplace_back(std::move(str));

Ответ 4

Во втором случае есть смысл сделать это. Рассмотрим этот код:

int main()
{
    std::vector<std::string> bar;
    std::string str("some_string");
    bar.emplace_back(std::move(str));
    // bar.emplace_back(str);
    std::cout << str << std::endl;
}

Если вы измените комментарий на строку выше, вы увидите, что в итоге вы получите две копии "some_string" (один в bar и один в str). Так что это что-то меняет.

В противном случае первый перемещает временное, а третье перемещает константный строковый литерал. Он ничего не делает.