Rosanna Trevisan
Rosanna Trevisan

Reputation: 452

Move semantic for method redundant move call

Say that I have this class

struct Test {
  std::string a;
  void setA(std::string&& input) {
    a = input;
    input = "";
  }
}

Here I move the content of input into a and then I leave input on a safe destructable state. This is the classic use of the move semantic where I can avoid copies.

Now say that I have this class

struct Test {
  std::string a;
  void setA(std::string&& input) {
    DoSomeWork(input);
  }
  void DoSomeWork(std::string&& other) { /* ... */}
}

Is this still correct? or should I use DoSomeWork(std::move(input));? I do not know if the move is required or not in this case.


Note. In case 1 I receive an rvalue reference as input and I use the classic approach.

void setA(std::string&& input) {
  a = input;   //input is an rvalue referece and I "transfer" its content into a
  input = "";  //maybe useless but in some books (including c++ primer) I've seen that it's good practice to "reset" the moved-from object and leave it in a destructable state!

I understand that. What I cannot understand is:

void setA(std::string&& input) {
  //recall that DoSomeWork accepts a std::string&&
  DoSomeWork(input);
}

Here if I want to pass input to the function and move it I do not know if std::move is required. I already have an rvalue reference so is the move process automatic? or the std::move call is required?

Upvotes: 2

Views: 303

Answers (3)

songyuanyao
songyuanyao

Reputation: 172924

Here I move the content of input into a

No. You're copying the content of input to a.

You might confuse types and value categories. As a named parameter, input is an lvalue; given a = input;, the copy assignment operator, but not move assignment operator will be invoked.

and then I leave input on a safe destructable state.

It's superfluous, the work should be left to the move assignment operator of std::string.

Yes you should use std::move to convert input to rvalue, e.g.

void setA(std::string&& input) {
  a = std::move(input); // input is move assigned to a, and left with undetermined but valid state
}

and

void setA(std::string&& input) {
  DoSomeWork(std::move(input));
}

Upvotes: 3

lubgr
lubgr

Reputation: 38277

In the function signature

void setA(std::string&& input) { /* ... */ }

the variable input is bound to an rvalue, but is an lvalue itself. When you want to move-construct another object from it, you need to cast it to an rvalue beforehand:

void setA(std::string&& input) {
    a = std::move(input);
}

Note that it's unnecessary now to set input to an empty string now. You simply shouldn't touch this variable anymore. The same reasoning is true when you pass input to some other function:

void setA(std::string&& input) {
   /* Cast to rvalue necessary to preserve "rvalue-ness" of the argument: */
   DoSomeWork(std::move(input));
}

Upvotes: 3

Nicol Bolas
Nicol Bolas

Reputation: 473447

Here I move the content of input into a and then I leave input on a safe destructable state. This is the classic use of the move semantic where I can avoid copies.

No, you are not; that is a copy. Unless you explicitly use std::move on a named &&, it's always a copy.

Also, when you correctly perform a move, it is not your responsibility to put the previous object in a "safe destructable state"; that's the responsibility of the move constructor/assignment operator.

Is this still correct?

If by "correct" you mean "perform a move", no. Again, if you want to move from a named variable, you must use std::move on it. This includes passing it to rvalue reference parameters.

The only exception being a return <named_variable>; statement, and even then, "named_variable" has to name a value, not a reference (though C++20 may allow rvalue reference variables to be implicitly moved from in this way).

Upvotes: 1

Related Questions