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
). Так что это что-то меняет.
В противном случае первый перемещает временное, а третье перемещает константный строковый литерал. Он ничего не делает.