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

nodeover
  • 301
  • 1
  • 2
  • 11
  • We usually spell it "rvalue", all-lower-case. –  Jan 30 '18 at 10:05
  • Being able to pass an lvalue `Point` seems reasonable to me. – chris Jan 30 '18 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 Jan 30 '18 at 10:06
  • 3
    Your definition of an rvalue is flawed. [I'm so sorry](https://godbolt.org/g/RJWvEe). The rules are a bit more complicated. – Rakete1111 Jan 30 '18 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 Jan 30 '18 at 10:09
  • It's spelled "rvalue". There is no camelCase in C++. – Jive Dadson Jan 30 '18 at 11:32

2 Answers2

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().

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 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 Jan 30 '18 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 Jan 30 '18 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!
 }
David
  • 93
  • 2
  • 7
  • 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 Jan 30 '18 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](http://en.cppreference.com/w/cpp/utility/move)). 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 Jan 30 '18 at 17:26