Reputation: 452
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
Reputation: 172924
Here I move the content of
input
intoa
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
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
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