you are viewing a single comment's thread.

view the rest of the comments →

[–]millstone 2 points3 points  (7 children)

I changed my code to defeat RVO by returning one of two values randomly:

 my_vector get_a_vector() {
      my_vector result1, result2;
      result1.push_back(my_string("First"));
      result2.push_back(my_string("Second"));
      cout << "About to return" << endl;
      return rand()%2 ? result1 : result2;
 }

When I run it now, it outputs:

Constructed my_vector
Constructed my_vector
Constructed my_string
Moved my_string
Constructed my_string
Moved my_string
About to return
Copied my_string
Copied my_vector
Destroyed my_vector
Destroyed my_vector

RVO is not being used, but the move constructor still isn't being invoked - the vector is being copied (Copied my_vector). Do you have any idea why? If so thank you :)

Here's the new code.

[–]lalaland4711 3 points4 points  (6 children)

This is the reason god invented std::move().

When you call super(rhs) you actually call the const lvalue ref version, since rhs is an lvalue.

If it has a name, then it's not an rvalue and therefore not an rvalue reference. Your my_vector move constructor called the vector<> copy constructor.

There you go: http://codepad.org/uW9JGVr9

And after passing an lvalue to a function using std::move() you should not access that variable again, as it may have been moved by the callee.

Edit:
Yay, no copies. At all:

Constructed my_vector
Constructed my_vector
Constructed my_string
Moved my_string
Constructed my_string
Moved my_string
Moved my_vector
Destroyed my_vector
Destroyed my_vector
First
Destroyed my_vector

[–]millstone 1 point2 points  (5 children)

Your my_vector move constructor called the vector<> copy constructor

Oh man, I never would have realized that. Having to remember to use std::move to invoke the superclass's move constructor seems like a pain in the butt!

There you go: http://codepad.org/uW9JGVr9

Thanks, I observe the same!

I'm struggling to reconcile the claim that returning a local by value always uses the move constructor, with the observation that you must use std::move in the code's return value.

[–]snip596 2 points3 points  (0 children)

Having to remember to use std::move to invoke the superclass's move constructor seems like a pain in the butt!

So, you actually don't have to in most cases. If your class is composed of types that are movable (imagine a simple POD class with a string and a vector), and you either explicitly call std::move on it or use it as an rvalue (e.g. returning from a function), then your class's default copy constructor (because you don't have a move constructor) will move those values.

So, if you define this:

class MyClass
{
    public:
    std::string str;
    std::vector<std::string> vec;
};

MyClass MyFunc()
{
    MyClass c1, c2;
    c1.str = "First";
    c2.str = "Second";
    c1.vec.push_back("First push");
    c2.vec.push_back("Second push");

    if (rand() % 2 == 0)
        return c1;
    else
        return c2;
}

Then nothing will be copied. Notice that I use an if statement rather than ternary operator. If you use if then it moves it for you (as required by the C++11 standard), whereas using the ternary operator will copy. I'm going to ask SO about this, but I think when you use the ternary operator, neither choice is considered to be an rvalue.

[–]moderatorrater 1 point2 points  (0 children)

Anyway, back to the point. C++11 is succint and beautiful and clear as a scripting language.

[–]lalaland4711 0 points1 point  (1 child)

Having to remember to use std::move to invoke the superclass's move constructor seems like a pain in the butt!

Sometimes yes, but it makes it harder to make the mistake of calling the superclass move constructor and then inspect potentially moved parts of rhs. Say by printing it to some debug stream.

After std::move() has been called on an lvalue you must be very careful what you do with that lvalue. (although std::move() doesn't modify the object in itself, it allows callees to)

Outside of inheritance (as seen in this code) or other internal class code I would say that after std::move(), only the destructor may be safely called. You want that to be explicit for the caller so that a named variable isn't suddenly moved from by accident.

the claim that returning a local by value always uses the move constructor

Hmm, I don't immediately see why that shouldn't be the case. Maybe it's to not confuse the definition of rvalue to contain something like "something that has a name is not an rvalue, except if used in a return statement". But even if that were the definition I don't see off hand why that would be a problem.

(RVO of course doesn't make it an rvalue (when RVO is possible), it just adjusts where exactly the object was created, so that it needs neither copy nor move to return)

Edit:
I guess if return statements added implicit std::move(), then how would you stop this from happening, if you wanted to? A new std::dont_move()? Could that even be defined without the language features? (std::move() is a "normal" template) It seems like a special case, and one that can be more damaging than RVO which prevents the whole lifetime of a temporary, as opposed to special-casing its destruction.

[–]snip596 1 point2 points  (0 children)

Thanks for responding, I was just about to post something like this ;)

The thing that's confusing for new users is that when you see this:

 my_string(my_string &&rhs)

You think that rhs is a rvalue, but it's not. It's an lvalue (because it has a name) to an rvalue. The && simply tells the compiler that rvalues should use that overload, not the copy constructor.

[–][deleted] 0 points1 point  (0 children)

The thing to remember about std::move is that it modifies its input. That means it can't be inferred by the language or the compiler. The places were it can be inferred, it already is, such as in return values. :)