40

Is there any advantage of using std::vector::emplace_back and std::move together? or it is just redundant since std::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"));
Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160
  • 3
    The third move, at least, is pointless. That literal string there is const, so it cannot be moved. – H. Guijt Feb 15 '16 at 08:53

4 Answers4

41

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.

Felix Dombek
  • 13,664
  • 17
  • 79
  • 131
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • 1
    Could you please clarify "same as `push_back` in this case"? In which case exactly? Can `emplace_back` always be replaced with `push_back` if its argument is `move`d? – Felix Dombek Sep 26 '17 at 15:57
  • 1
    in the first and second case, `emplace_back` could be replaced with `push_back` – sp2danny Jan 14 '19 at 10:12
  • If `emplace_back` was replaced with `push_back`, would that not result in a copy rather than a move? – Hugo Burd Aug 08 '20 at 00:54
  • 1
    @HugoBurd late reply but no, `push_back` has a moving version – cebola Jul 26 '21 at 20:41
8

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.

David Haim
  • 25,446
  • 3
  • 44
  • 78
6

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));
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Stas
  • 11,571
  • 9
  • 40
  • 58
1

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.

Ami Tavory
  • 74,578
  • 11
  • 141
  • 185
  • 1
    With 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.. – Wolfgang Jan 14 '19 at 08:20
  • @Wolfgang Thanks, excellent point. `str.clear()` should [first be called](https://stackoverflow.com/questions/9168823/reusing-a-moved-container/9168917#9168917). Updated the answer. – Ami Tavory Jan 14 '19 at 08:27