0

I am learning c++ via the book from his creator, I understood that rValue is basically what can't be defined via it's address & you want to move it instead of copy.

Later in this book Stroustrup give an example of a Points struct

struct Point{
  int x,y;
}

struct Points{
  vector<Point> elem;
  Points(Point p0){elem.push_back(p0)};
  ...
}

Point x2{ {100,200} }

So my question is basically why in the constructor it does not use an rValue to move the sent value ?

Maybe like:

struct Points{
  vector<Point> elem;
  Points(Point&& p0){elem.push_back(p0)};
  ...
}

Is it more proper / less clear to do it?

regards

6
  • We usually spell it "rvalue", all-lower-case.
    – user2486888
    Commented Jan 30, 2018 at 10:05
  • Being able to pass an lvalue Point seems reasonable to me.
    – chris
    Commented Jan 30, 2018 at 10:05
  • 1
    Note that p0 in your second example is an lvalue which means that elem.push_back(p0) makes a copy of it. If the book is any good at all it will explain that.
    – nwp
    Commented Jan 30, 2018 at 10:06
  • 3
    Your definition of an rvalue is flawed. I'm so sorry. The rules are a bit more complicated.
    – Rakete1111
    Commented Jan 30, 2018 at 10:09
  • 3
    It's worth noting that Point being a really simple type made only of fundamental members, it does not matter.
    – Quentin
    Commented Jan 30, 2018 at 10:09

2 Answers 2

1

Moving values is especially useful when moving is cheaper than copying. As it happens, copying an int is super cheap. Your CPU will do it all the time, as it loads values from memory to registers, or between registers. As a result, for an int moving is the same as copying.

Your Point is two integers, and the same logic still holds. But vector<Point> is another matter. You'll find that sizeof(Points) is a small number. That's because it does not include sizeof(Point)*elem.size(), a quantity that can vary at runtime. And that's where moving becomes beneficial. Moving that vector moves the sizeof(Point)*elem.size().

2
  • Yes sorry the example is simpla, so do you suggest that if this operation was in a 100 000X for loop it could be a best way to use rvalue push_back but if the size of the data to move are limited the best way is to copy?
    – nodeover
    Commented Jan 30, 2018 at 13:31
  • @nodeover: The essence of a "move" is that you don't care about what happens to the source. That means swap can be implemented as a copy, as a swap, or even as a mix (some struct members copied, some swapped). All those operations result in the destination getting the right value. That's why moving int is done by copy - swapping them is more expensive. Doing that a 100.000 time sin a loop does not change that. But if you have a pointer to 50.000 integers and a pointer to 60.000 integers, swapping two pointers is by far the fastest operation.
    – MSalters
    Commented Jan 30, 2018 at 14:21
1

What exactly do you mean by "it does not use an rValue"? Do you mean p0 is not "sent" as rvalue or do you mean the call to push_back does not use an rvalue?

  1. Point&& p0 is initialized with an rvalue - so far so good.
  2. push_back (p0) copies an lvalue, what you probably wanted to do was: push_back (std::move (p0)).

There is a good mnemonic you could use in order to distinguish between lvalues and rvalues:

Whenever there is a name for an object, it is an lvalue, whenever an object does not have a name, it is an rvalue.

For example: call_to_some_function (std::string {"Hi there"});, here the string passed to the function does not have a name, it is an rvalue. The same applies to all temporaries.

 void some_function (std::string&& what)
 {
       do_something_with (what); // <- what is an lvalue
       do_something_else (std::move (what)); // <- turned into rvalue
       const std::string& foo = get_some_foo_string ();
       do_something_else (std::move (foo)); // <- No rvalue here!
 }
2
  • I like your explanation, it's clear. So does this mean that the rvalue can be use as a "rvalue" in the variable scope only?
    – nodeover
    Commented Jan 30, 2018 at 13:38
  • I don't understand this question - you can use rvalues everywhere, as soon as there is a name for the rvalue though it becomes an lvalue. You can create an rvalue from an lvalue, this is what std::move does (indicate that an object t may be "moved from" cppreference.com). Use std::move whenever you want to move instead of copy, like in your second code example, this will indeed move instead of copy if you write push_back (std::move (p0)). Your first example would copy and then move, since you take the parameter by value.
    – David
    Commented Jan 30, 2018 at 17:26

Not the answer you're looking for? Browse other questions tagged or ask your own question.