0

I have been wondering about that all day long and I can't find an answer to that specific case.

Main :

std::vector<MyObject*> myVector;
myVector.reserve(5);
myFunction(std::move(myVector));

myFunction :

void myFunction(std::vector<MyObject*> && givenVector){
    std::vector<MyObject*> otherVector = givenVector;
    std::cout << givenVector[0];
    // do some stuff
}

My questions are :

  1. in the main, is myVector destroyed by the function myFunction() because it is considered as an rvalue or does the compiler knows that it is also a lvalue and therefore performs a copy before sending it to myFunction ? What happens if I try to use the vector after the call to myFunction()?

  2. inside the function myFunction() , is the vector givenVector destroyed when affected to otherVector ? if so, what happens when I try to print it ? if not is it useful to use rvalue in this function ?

Arcyno
  • 4,153
  • 3
  • 34
  • 52

5 Answers5

1

Looks like duplicate.

  1. myVector is not destroyed by the function myFunction(). It's unspecifed what should happen in general case with class with stealen resources.

  2. givenVector is not destroyed when affected to otherVector. It's unspecifed what should happen in general case with class with stealen resources.

Community
  • 1
  • 1
M.L.
  • 728
  • 4
  • 12
0

If a function gets an argument by rvalue-reference, that does not mean it will be destructively used, only that it can be.

Destructive use means that the passed object is thereafter in some unspecified but valid state, fit only for re-initializing, mving, copying or destruction.

In the function, the argument has a name and thus is an lvalue.
To mark the place(s) where you want to take advantage of the licence to ruthlessly plunder it, you have to convert it to an rvalue-reference on passing it on, for example with std::move or std::forward, the latter mostly for templates.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
0

The code won't compile, since you try to bind an lvalue to an rvalue reference. You'll need to deliberately convert it to an rvalue:

myFunction(std::move(givenVector));

Simply doing this won't "destroy" the object; what happens to it depends on what the function does. Generally, functions which take rvalue references do so in order to move from the argument, in which case they might leave it in some valid but "empty" state, but won't destroy it.

Your code moves the vector to the local otherVector, leaving it empty. Then you try to print the first element of an empty vector, giving undefined behaviour.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
0
  1. No copy is performed. What happens to myVector depends on what myFunction does with it. You should consider objects that have been moved from as either being the same or being empty. You can assign new values and keep using it or destroy it.

  2. myVector is fine. It is an lvalue and otherVector makes a copy of it. You most likely wanted to write otherVector = std::move(myVector);, in which case myVector should be empty. If you have an old implementation of the STL (that does not know about move semantics) a copy is performed and myVector is not changed. If that makes sense is for you to decide. You moved a given vector to a new vector, which can be useful. Printing an empty vector is not so useful.

nwp
  • 9,623
  • 5
  • 38
  • 68
0

In order to be compilable, you should apply a std::move to your vector before you pass it to the function (--at least if no further overloads exists):

myFunction(std::move(myVector));

Then, inside the function, by

std::vector<MyObject*> otherVector = std::move(givenVector);  

the move constructor of std::vector is called which basically moves all the content out of the vector (note however again the std::move on the right-hand side -- otherwise you'll get a copy). By this, the vector is not "destroyed". Even after the move it is still alive, yet in an unspecified state.

That means that those member functions which pose no specific condition on the state of the vector might be called, such as the destructor, the size() operator and so on. A pop_back() or a derefencing of a vector element however will likely fail.

See here for a more detailed explanation what you still can do with a moved-from object.

Community
  • 1
  • 1
davidhigh
  • 14,652
  • 2
  • 44
  • 75
  • do you mean I have to call `std::move` twice ? and what about the case `myFunction(new vector<...>())` : should I also call `std::move` here ? like `myFunction(std::move(new vector<...>()))` ? – Arcyno May 12 '15 at 13:32
  • @Arcyno: Yes, you have to call `move` twice: one time in main when you pass the vector to the function, and the second time inside the function. See the comment to your function above which states *if a variable has a name, it's an lvalue*. Both your vectors (in main and inside the function) "have a name". So you have to cast them to an rvalue, which is what `move` does. – davidhigh May 12 '15 at 14:31
  • @Arcyno: the second part is not advisable. `new vector<...>` returns a *pointer* to the heap where the vector has been allocated, so your function in the OP won't take it. Also, in general, you shouldn't pass pointers by reference -- and in no way by an rvalue reference. You gain nothing, as a pointer-move is not more than a copy. Moreover, you shouldn't use `new` at all. What you possibly wanted to do is to create a `std::unique_ptr` and pass this by a move. That would be valid. – davidhigh May 12 '15 at 14:39
  • Thanks for you explanations. But I am not sure I properly understood what you meant when you said I should not use `new`.. What if I would give to a function a new object as rvalue : `function(new myObject(arguments));` ? – Arcyno May 12 '15 at 14:50
  • 1
    @Arcyno: Starting from C++14, the use of `new` is commonly discouraged. For pointers to heap objects one should instead use `shared_ptr` and `unique_ptr` and create the objects via `std::make_shared` and `std::make_unique`. With this `new` simply isn't needed anymore, instead one lets specialized classes do the memory management (particularly, when to call `delete`). – davidhigh May 12 '15 at 15:02
  • @Arcyno: ... but that is more or less general. For your application, you should not pass the pointer created by new via a reference. So, instead of setting up your function as `function(myObject* const&)` or `function(myObject* &&)`, you really should pass the plain pointer, i.e. `function(myObject*)`. The pointer copy is so cheap that you gain nothing by moving it or taking a reference to it. – davidhigh May 12 '15 at 15:09