std::vector::emplace_back and std::move
Solution 1
In the second version, there is an advantage. Calling emplace_back
will call the move constructor of std::string
when std::move
is used, which could save on a copy (so long as that string isn't stored in a SSO buffer). Note that this is essentially the same as push_back
in this case.
std::move
in the first version is unnecessary, as the string is already a prvalue.
std::move
in the third version is irrelevant, as a string literal cannot be moved from.
The simplest and most efficient method is this:
bar.emplace_back("some_string");
That requires no unnecessary std::string
constructions as the literal is perfect-forwarded to the constructor.
Solution 2
emplace_back
calls to somehthing like
new (data+size) T(std::forward<Args>(args)...);
if args
are basic - non - rvalue-referenced std::string
, the expression will compile to
new (data+size) std::string(str); //str is lvalue - calls std::string::string(const string& rhs)
meaning the copy constructor will take place.
but, if you use std::move
on str
, the code will compile to
new (data+size) std::string(str); //str is r-value reference, calls std::string::string(string&& rhs)
so move semantics takes place. this is a huge performance gain.
do note, that str
is lvalue, it has a name, so in order to create r-value-reference from it, you must use std::move
.
in the example
vec.emplace_back("some literal");
the code will compile to
new (data+size) std::string("literal"); //calls std::string::string(const char*);
so no temporaries.
the third example is nonsense. you cannot move literals.
Solution 3
The whole idea of emplace_back
is to get rid of copying and moving operations. You just need to pass input parameters of std::string
into emplace_back
. A std::string
object will be constructed inside emplace_back
method.
bar.emplace_back("some_string");
If you already have a string, it makes sense to use std::move
. A std::string
object will be constructed inside emplace_back
by moving data from str
.
std::string str("some_string");
bar.emplace_back(std::move(str));
Solution 4
There is a point of doing so in the second case. Consider this code:
int main()
{
std::vector<std::string> bar;
std::string str("some_string");
bar.emplace_back(std::move(str)); str.clear();
// bar.emplace_back(str);
std::cout << str << std::endl;
}
If you change the comment to the line above, you can see that you will end up with two copies of "some_string" (one in bar
and one in str
). So it does change something.
Otherwise, the first one is moving a temporary, and the third is moving a constant string literal. It does nothing.
Humam Helfawi
Computer-Vision Research Engineer and C++ Lover.
Updated on January 14, 2020Comments
-
Humam Helfawi over 4 years
Is there any advantage of using
std::vector::emplace_back
andstd::move
together? or it is just redundant sincestd::vector::emplace_back
will do an inplace-construction?Cases for clarification:
std::vector<std::string> bar;
First:
bar.emplace_back(std::move(std::string("some_string")));
Second:
std::string str("some_string"); bar.emplace_back(std::move(str));
Third:
bar.emplace_back(std::move("some_string"));
-
Felix Dombek over 6 yearsCould you please clarify "same as
push_back
in this case"? In which case exactly? Canemplace_back
always be replaced withpush_back
if its argument ismove
d? -
Wolfgang over 5 yearsWith your code (without the commented one) dont you move away the str resulting str is dangling then passing it to cout? This seems dangerous for me..
-
Ami Tavory over 5 years@Wolfgang Thanks, excellent point.
str.clear()
should first be called. Updated the answer. -
sp2danny over 5 yearsin the first and second case,
emplace_back
could be replaced withpush_back
-
Hugo Burd almost 4 yearsIf
emplace_back
was replaced withpush_back
, would that not result in a copy rather than a move? -
cebola almost 3 years@HugoBurd late reply but no,
push_back
has a moving version