Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

understanding c++11 rvalues, move semantics and performance [duplicate]

Possible Duplicate:
What happens if I return literal instead of declared std::string?

Consider the following code

string getName () {
    return "meme";
}

string name = getName();

The function getName() returns a temporary object. In C++03, I understand the copy constructor of string gets called and the temporary object is destroyed. Actually it seems that the compiler (at least in GCC 4.7) optimizes line 5 by not creating the object name but replacing it with the temporary object itself and not destroying the temporary object. (I tried with a MyVector class, not std::string)

As defined in C++11 standards,

  1. Is getName() returning an rvalue?

  2. In line 5 above, which constructor of string gets called (move or copy) ? Should I necessarily call std::move() for the move constructor to get called?

  3. With move semantics, is it less efficient then the "copy elision" optimization provided by the compiler?

like image 751
Kiran Mohan Avatar asked Dec 23 '12 15:12

Kiran Mohan


1 Answers

  1. Functions don't return rvalues or lvalues. The value categories apply to expressions. So an expression that calls a function may be an rvalue or lvalue. In this case, the expression getName() is an rvalue expression because the function getName returns an object by value. This comes from §5.2.2/10:

    A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.

    Your functions result type is not an lvalue or rvalue reference, so the function call is a prvalue. prvalue expressions are a subset of rvalue expressions.

  2. The move constructor will be used (unless it is elided, which it may be). That's because getName() is an rvalue, so the constructor of std::string that takes an rvalue reference will better match the argument. Note that even if the move construction is elided, the move constructor must still be accessible. That is, the code must be compilable even if it is not elided.

  3. In general, the optimization of copy or move elision will completely get rid of any copying or moving. So of course it's faster than actually doing a move. If a move is elided, literally nothing happens. There will be no code emitted for that move. The compiler achieves this by directly constructing the object in the location it would be copied or moved to.

It's worth mentioning that this could also be equivalently optimized:

string getName () {
  std::string str("meme");
  return str;
}
 
string name = getName();

Here, two moves will be elided (involving what is commonly known as Named Return Value Optimization). There are two points to consider here. First, return str; meets the criteria for copy/move elision (§12.8/31):

This elision of copy/move operations, called copy elision, is permitted in the following circumstances (which may be combined to eliminate multiple copies):

  • in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-unqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function’s return value
  • ...

Second is that, although str is an lvalue, it will still be moved from because it fits a special case given by the standard (§12.8/32):

When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

like image 149
Joseph Mansfield Avatar answered Nov 12 '22 22:11

Joseph Mansfield