move constructor and std::move confusion - c++

I am reading about the std::move, move constructor and move assignment operator.
To be honest, all I got now is confusion. Now I have a class:
class A{
public:
int key;
int value;
A(){key = 3; value = 4;}
//Simple move constructor
A(A&& B){ A.key = std::move(B.key);
A.value = std::move(B.value);}
};
I thought B is an rvalue reference, why you can apply std::move to an ravlue reference's member?
After B.key and B.value have been moved, both have been invalidated, but how B as an object of class A gets invalidated?
What if I have A a(A()), A() is apparently an rvlaue, can A() be moved by std::move and why?
Similarly, if I have a function
int add(int && z){
int x = std:move(z);
int y = std:move(z);
return x+y;
}
What if I call add(5), how can 5 be moved and why?
And notice that z has been moved twice, after z has been moved first time, it has been invalidated, how can you move it again?
When defining foo (T && Z )(T, Z can be anything), in the body of the definition Why on earth I should use std::move(Z) since Z is already passed by an rvalue reference and when should I use std::move?

std::move does not move anything, but "marks" its argument to be a rvalue reference. Technically, it converts the type to a rvalue reference. Then, the rvalue reference it's being moved by the corresponding move constructor or move assignment operator. For objects that contain only members with trivial move ctors/assignment operators, the move ctor/assignment operator is trivial and simply copies. In general, the move ctor/assignment operator of the object calls the move ctor/assignment operator of all its members.
So, whenever you write
int x = 10;
int y = std::move(x);
on the right hand side of the assignment y = std::move(x), you have a rvalue reference of type int&&. However, int does not have a non-trivial move ctor, and the rvalue is simply copied into y, nothing is changed in x.
On the other hand,
string s = "some string";
string moved_s = std::move(s); // here we tell the compiler that we can "steal" the resource of s
is different. The move constructor of moved_s kicks in, and "steals" (i.e. swaps internal pointers etc) the resource of s, because the latter is a rvalue reference. At the end, s will not contain any element.

B is the name of an object. Once a reference has been bound, it names an object. The distinction "rvalue reference", "lvalue reference" and "named object" only applies to how the name can be bound before you got this far.
B.key is the name of a variable in the object which was supplied as argument to this function call.
"invalidated" is not part of the standard terminology for moving. Standard library objects are left in an unspecified state after being moved out of; but that's not what's going on here.
The line A.key = std::move(B.key) invokes the built-in definition of assignment for an int (this is a simple assignment, not a function call), which is just a copy. So B.key retains its value.
For A(B()) to compile, B must be a typename which you haven't defined yet. (Did you mean A(A()) ? If so, then the answer is "Yes").
See 2
Use std::move(Z.foo) whenever you want to move out of Z.foo instead of copying from Z.foo.

Related

Pass by value/reference/rvalue with a std::move(str) arg

I have the following code:
//void func(const std::string &&i){
//void func(const std::string &i){
void func(const std::string i){
std::string val{i};
}
int main()
{
std::string d = "asdf";
func(std::move(d));
std::cout << d << std::endl;
}
When i is pass-by-value, d becomes empty, but d retains its form if we're passing by reference or by r-value reference. Could someone explain what is going on?
I understand that std::move doesn't actually move anything, but rather makes the variable it takes in moveable by casting it to an xvalue.
As an aside why does the code in the current state compile if d is cast to an x-value? func is currently set to take in pass by value arguments, and not by rvalue reference.
When i is pass-by-value, d becomes empty,
To be accurate, d will be in some valid state not specified in the C++ standard. Empty is one possibility.
std::move itself never causes the move constructor to be called directly. Neither does binding an rvalue reference to an object cause move constructor to be called directly.
Only initialising an object with a non-const rvalue will cause the argument to be moved from. In the example, std::string i is initialised with a non-const rvalue and the move constructor will be called.
As an aside why does the code in the current state compile if d is cast to an x-value?
Because the type has a (non-deleted) move constructor. Therefore the argument can be initialised from an rvalues.
I had thought if we had std::string i, a copy of the rvalue reference is made.
std::string i is not a reference. It is a variable of type std::string and as such there is an object of type std::string associated with the variable. That object is initialised with the expression that is passed into the function as argument.
Also, if I observe that the output of d is still the same as prior to applying std::move, what does this mean in this case?
If you call the uncommented version of the function with an rvalue, then the argument will be moved from. If the value is same as it was, then it simply means that the value is the same. You cannot assume that the value will be the same nor that it won't be the same.
Does it mean that d is still occupying the space it originally occupied?
Assuming that by "space" you mean the storage where the variable is, then of course it is still occupying the same storage. The address of an object never changes through the lifetime of the object.
void func(const std::string &&i)
This signature will not move anything, because the reference is to a const object. Remove the const, and it'll work. But only if you std::move the parameter i again inside the function. This is because anything that has a name is an lvalue, whether the parameter was declared as & or &&. See this answer.
void func(const std::string &i)
This will copy, as you probably already know. However, it behaves similarly to the ptevious one in that if you drop the const and do std::move( i ) inside the function, it'll actually move. This is because, as you noted, move is a cast and the compiler will listen to you and do exactly what you say when you cast, regardless of what you intended.
void func(const std::string i)
This moves in your example because here, i is an entirely new string. The outside string d gets moved into i. However, you still have to drop the const and use std::move( i ) if you want to move i into val.

Why do overloaded move assignment operators return lvalue reference instead of rvalue reference?

So this is really just something I can't make sense of semantically. Assignment chaining make sense for copy semantics:
int a, b, c{100};
a = b = c;
a, b, and c all are 100.
Try a similar thing with move semantics? Just doesn't work or make sense:
std::unique_ptr<int> a, b, c;
c = std::make_unique<int>(100);
a = b = std::move(c);
This doesn't even compile, because a = b is a copy assignment, which is deleted. I could make an argument that after the final expression executes, *a == 100 and b == nullptr and c == nullptr. But this isn't guaranteed by the standard. This doesn't change much:
a = std::move(b) = std::move(c);
This still involves copy assignment.
a = std::move(b = std::move(c));
This one actually does work, but syntactically it is a huge deviation from copy assignment chaining.
Declaring an overloaded move assignment operator involves returning an lvalue reference:
class MyMovable
{
public:
MyMovable& operator=(MyMovable&&) { return *this; }
};
But why isn't it an rvalue reference?
class MyMovable
{
public:
MyMovable() = default;
MyMovable(int value) { value_ = value; }
MyMovable(MyMovable const&) = delete;
MyMovable& operator=(MyMovable const&) = delete;
MyMovable&& operator=(MyMovable&& other) {
value_ = other.value_;
other.value_ = 0;
return std::move(*this);
}
int operator*() const { return value_; }
int value_{};
};
int main()
{
MyMovable a, b, c{100};
a = b = std::move(c);
std::cout << "a=" << *a << " b=" << *b << " c=" << *c << '\n';
}
Interestingly this example actually works as I expect, and gives me this output:
a=100 b=0 c=0
I'm not sure if it shouldn't work, or why it does, especially since formally move assignment isn't defined this way. Quite frankly this just adds more confusion in an already-confusing world of semantic behaviors for class types.
So I've brought up a few things here, so I'll try to condense it into a set of questions:
Are both forms of assignment operators valid?
When would you use one or the other?
Is move assignment chaining a thing? If so, when/why would you ever use it?
How do you even chain move assignment that returns lvalue references in a meaningful way?
Yes, you are free to return whatever you want from an overloaded assignment operator and so your MyMovable is fine, but users of your code may be confused by the unexpected semantics.
I don't see any reason to return a rvalue-reference in the move assignment. A move assignment usually looks like this:
b = std::move(a);
and after that, b should contain the state of a, while a will be in some empty or unspecified state.
If you chain it in this way:
c = b = std::move(a);
then you would expect b to not loose its state, because you never applied std::move to it. However if your move assignment operator returns by rvalue-reference, then this will actually move the state of a into b and then the left-hand assignment operator would also call the move assignment, transferring the state of b (which was a's before) to c. This is surprising, because now both a and b have empty/unspecified state. Instead I would expect a to be moved into b and copied into c, which is exactly what happens if the move assignment returns a lvalue-reference.
Now for
c = std::move(b = std::move(a));
it works as you expect, calling the move assignment in both cases and it would be clear that the state of b is moved as well. But why would you want to do that? You could have transferred a's state to c directly with c = std::move(a); without clearing b's state in the process (or worse putting it in a not directly usable state). Even if you want that, it would be clearer stated as a sequence
c = std::move(a);
b.clear(); // or something similar
As for
c = std::move(b) = std::move(a)
at least it is clear that b is moved from, but it seems as if the current state of b was moved, rather than the one after the right-hand move and again, the double move is redundant. As you noticed this still calls the copy-assignment for the left-hand, but if you really want to make this call the move assignment in both cases, you can return by rvalue-reference and to avoid the issue with c = b = std::move(a) explained above, you would need to differentiate on the value category of the middle expression. This can be done e.g. in this way:
MyMovable& operator=(MyMovable&& other) & {
...
return *this;
}
MyMovable&& operator=(MyMovable&& other) && {
return std::move(operator=(std::move(other)));
}
The & and && qualifiers signify that the particular overload should be used if the expression that the member function is called on is a lvalue or rvalue.
I don't know whether there is any case you would actually want to do that.
Are both forms of assignment operators valid?
They are well-formed and have well defined behaviour. But returning an rvalue reference to *this from a function that isn't rvalue qualified would be non-conventional and probably not a good design.
It would be very surprising if:
a = b = std::move(c));
caused b to be moved from. Surprisingness is not a good feature for an API.
When would you use one or the other?
In general, you'd never want to return an rvalue reference to *this except from rvalue qualified function. From rvalue qualified function, it's preferable to return an rvalue reference or maybe even a prvalue depending on context.
Is move assignment chaining a thing? If so, when/why would you ever use it?
I've never seen it used, and I cannot think of an attractive use case.
How do you even chain move assignment that returns lvalue references
Like you showed, with std::moves.
in a meaningful way?
I'm not sure if there is a way to introduce meaning to it.
Are both forms of assignment operators valid?
assignment operator are mostly regular method and doesn't need to return lvalue reference to self type, returning void, char would also be valid.
To avoid surprise, we try to mimic built-in and so to allow chaining assignment, we return lvalue reference to self type.
When would you use one or the other?
I would personally only use MyMovable& operator=(MyMovable const&)
Is move assignment chaining a thing? If so, when/why would you ever use it?
I don't thing so.
but to allow syntax a = std::move(b) = std::move(c);, you might do:
MyMovable& operator=(MyMovable const&) = delete;
MyMovable&& operator=(MyMovable&& other) &&;
MyMovable& operator=(MyMovable&& other) &;
Are both forms of assignment operators valid?
According to the standard, I believe so, but be very careful. When you return an rvalue reference from operator=, you're saying that it can be freely modified. This can easily result in surprising behaviour. For example,
void foo(const MyMovable& m) { }
void foo(MyMovable&& m) {
m.value_ = 666; // I'm allowed to do whatever I want to m
}
int main() {
MyMovable d;
foo(d = MyMovable{ 200 }); // will call foo(MyMovable&&) even though d is an lvalue!
std::cout << "d=" << *d << '\n'; // outputs 666
}
The proper way to fix this would probably be to define your operator= like this:
MyMovable&& operator=(MyMovable&& other) && {...
Now this only works if *this is already an rvalue, and the example above will not compile since it's using operator= on an lvalue. However, this then doesn't allow your chaining move operators to work. I'm not sure how to allow both move operator chaining whilst defending against behaviour like in my example above.
When would you use one or the other?
I don't think I'd ever return an rvalue reference to *this. It's prone to surprising the caller, as in the example above, and I'm not really sure that enabling move assignment chaining is something I would really want to do.
Is move assignment chaining a thing? If so, when/why would you ever use it?
I don't ever use assignment chaining. It saves a few characters but I think it makes the code less obvious and, as this question demonstrates, can be a little tricky in practice.
How do you even chain move assignment that returns lvalue references in a meaningful way?
If I had to do this, I would use the form you used above: a = std::move(b = std::move(c)); This is explicit and obvious.

Does this copy the vector?

If I have the following code, is the vector copied?
std::vector<int> x = y.getTheVector();
or would it depend on whether the return type of getTheVector() is by reference?
or would I just need to use:
std::vector<int>& x = y.getTheVector();
or, would I need to do both?
std::vector<int> x = y.getTheVector();
always makes a copy, regardless of the return type of y.getTheVector();.
std::vector<int>& x = y.getTheVector();
would not make a copy. However, x will be valid as long as y.getTheVector() returns a reference to an object that is going to be valid after the function returns. If y.getTheVector() returns an object created in the function, x will point to an object that is no longer valid after the statement.
std::vector<int> x = y.getTheVector();
This is copy-initialization. There are three possible scenarios:
The return value of getTheVector() is a lvalue reference. In this case, the copy constructor is always invoked.
The return value of getTheVector() is a temporary. In this case, the move constructor may be called, or the move/copy may be completely elided by the compiler.
The return value is a rvalue reference (usually a terrible idea). In this case, the move constructor is called.
For this line,
std::vector<int>& x = y.getTheVector();
This only compiles if getTheVector returns a lvalue reference; a temporary cannot be bound a non-const lvalue reference. In this case, no copy is ever made; but the lifetime problem may be tricky.
std::vector<int> x = y.getTheVector();
Your first example does indeed copy the vector, regardless of whether the "getTheVector" function returns a vector or a reference to a vector.
std::vector<int>& x = y.getTheVector();
In your second example, however, you are creating a reference, so the vector will NOT be copied.

C++11 move and reassigning

This code does compile but I'm just beginning studying C++11 and I can't understand what's happening here behind the scenes.
void func(int&& var)
{
int var2(var);
}
void main()
{
int var1 = 22;
func(move(var1));
}
My guess: move(var1) returns a r-value of var1 (probably its data) and the func function is initializing var2 with the r-value of var1. But why is var1 still valid after the func call? Shouldn't it have an invalid value because its temp value has been re-assigned to var2?
There are several issues here.
One is that you're working with an int. And for an int a copy is just as fast as a move. So it's perfectly reasonable for a move to be implemented as a copy.
There is no requirement that move construction (or assignment) alter the value of the thing being moved from. It's a logical error in your code to continue to treat it as if it had a useful value, but it's not required that the value become useless.
The second is that your code as written doesn't actually move anything. Simply using ::std::move does not result in something being moved. It's just a nice way to turn an lvalue into an rvalue so that something can be moved. In your case, var has a name, so it's an lvalue. Your initialization of var2 in func is actually a copy, not a move. If you had written it int var2(move(var)); it would then be a move and var1 in main would be potentially invalidated at that point.
To reiterate, the ::std::move function is just there to signal to people reading your code that a move may happen and that the value of the variable after that cannot be counted on. It doesn't actually move anything.
Here is a marked up version of your code:
// func requires a temporary argument, something that can be moved from
void func(int&& var)
{
int var2(var); // Doesn't actually call int(int &&), calls int(int &)
// int var2(move(var)); // Would actually call int(int &&) and a move would
// happen then at this point and potentially alter the
// value of what var is referencing (which is var1
// in this code).
}
void main()
{
int var1 = 22;
func(move(var1)); // Tell people that var1's value may be unspecified after
// Also, turn var1 into a temporary to satisfy func's
// signature.
}
Since your code, as written, does not result in any moves happening, here is a version that does definitely move something somewhere:
#include <vector>
#include <utility>
using ::std;
// Still require an rvalue (aka a temporary) argument.
void func(vector<int>&& var)
{
// But, of course, inside the function var has a name, and is thus an lvalue.
// So use ::std::move again to turn it into an rvalue.
vector<int> var2(move(var));
// Poof, now whetever var was referencing no longer has the value it used to.
// Whatever value it had has been moved into var2.
}
int main()
{
vector<int> var1 = { 32, 23, 66, 12 };
func(move(var1)); // After this call, var1's value may no longer be useful.
// And, in fact, with this code, it will likely end up being an empty
// vector<int>.
}
And, of course, this way of writing it is silly. There are sometimes reasons to specify that an argument be a temporary. And that's usually if you have one version of a function that takes a reference, and another that takes an rvalue reference. But generally you don't do that. So, here's the idiomatic way to write this code:
#include <vector>
#include <utility>
using ::std;
// You just want a local variable that you can mess with without altering the
// value in the caller. If the caller wants to move, that should be the caller's
// choice, not yours.
void func(vector<int> var)
{
// You could create var2 here and move var into it, but since var is a
// local variable already, why bother?
}
int main()
{
vector<int> var1 = { 32, 23, 66, 12 };
func(move(var1)); // The move now does actually happen right here when
// constructing the argument to func and var1's value
// will change.
}
Of course, giving a name to var1 in that code is kind of silly. Really, it should just be written this way:
func(vector<int>{ {32, 23, 66, 12} });
Then you're just constructing a temporary vector right there and passing it into func. No explicit use of move required.
There is nothing to be moved from an int hence a copy is created. For the types that have move constructor or move assignment operator has specific purpose of moving the underlying resources, then there is a change other wise it is a copy.
The std::move will cast an lvalue to a rvalue so that some resources(objects on heap or file handles) can be moved out to other object however the actual movement happens in move constructor or move assignment operator.
For example consider vector in standard library which has move copy constructor and move assignment operator that does some explicit work to move resources across.
Having said this I believe that the way the type is moved inside function funcis not proper way to attempt a move. int&& var is the reference to rvalue (casted with std::move) however var (by name) is like a lvalue and int var2(var); would anyway not move the var to var2 and it will be COPY. The correct way to attempt a move would be int var2(std::move(var)); I mean if a type has move constructor which can move the resources you will have to use like that.
void func(int&& var) //2. Var is a reference to rvalue however the by name the var is a lvalue and if passed as it would invoke copy constructor.
{
int var2(std::move(var)); // 3. hence to invoke move constructor if it exists the correct attempt would be to case it to rvalue again as move constructors take rvalue. If the move constructor does not exists then copy is called.
}
void main()
{
int var1 = 22;
func(move(var1)); //1. Cast the type to rvalue so that it can be passed to a move copy contructor or move assigment operator.
}
The expression var1 is an lvalue. Applying std::move(var1) gives you an rvalue referring to the same object. You then bind this rvalue to the int&& called var.
The expression var is also an lvalue. This is because any expression which is a named variable is an lvalue (even if its type is an rvalue reference). You then initialise var2 with the value of var.
So all you've done is copied the value from var1 to var2. Nothing has been moved at all. In fact, you cannot even move from a fundamental type like an int. Attempting to initialise or assign from a temporary int will just copy its value and not move anything.
Even if you were using a type T that had a move constructor and assignment operator, your code wouldn't move anything. That's because the only non-reference initialisation you do is the line int var2(var);, but here var is an lvalue. That means the copy constructor will be used instead.
The simplest way to move from var1 to var2 would be to do this:
void func(T var)
{
T var2(move(var));
}
void main()
{
T var1(22);
func(move(var1));
}
This will move from var1 to create var and then it will move from var to create var2.
You could do this in almost the same way, except change var back to an rvalue reference, but I don't recommend it. You would have to document that the rvalue being passed will be invalidated by the internal moving.
move(var1) returns a r-value of var1 (probably its data)
No, move(var1) returns an rvalue reference, referring to var1.
and the func function is initializing var2 with the r-value of var1
func copies var1 to var2. If var1 were of a type with a move constructor, then the move constructor would be called to initialize var2. But it is not.
But why is var1 still valid after the func call?
Because copying an int doesn't make it invalid.
Shouldn't it have an invalid value?
There's no special "invalid value" of int. Using an uninitialized object has undefined behavior, but this object is not uninitialized.
As others have stated, your example uses a primitive type, which in essence invokes a copy, because a move just simply cannot occur here. However, I've created an example where a move can occur, using std::string. Its guts can be moved out from under, and you can see in this example that is what happens.
#include<iostream>
#include<string>
#include<utility>
void func(std::string&& var)
{
// var is an lvalue, so call std::move on it to make it an rvalue to invoke move ctor
std::string var2(std::move(var));
std::cout << "var2: " << var2 << std::endl;
}
int main()
{
std::string var1 = "Tony";
std::cout << "before call: " << var1 << std::endl;
func(std::move(var1));
std::cout << "after call: " << var1 << std::endl;
}
Output:
before call: Tony
var2: Tony
after call:
You can see that var1 has indeed been moved from and it no longer contains any data, however it is only in an unspecified state per the Standard and is still reusable.
Live Example

Intuitive understanding of functions taking references of references [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What does T&& mean in C++11?
For some reason, this is eluding my intuition, and I cannot find any explanation on the internet. What does it mean for a C++ function to take a reference of a reference? For example:
void myFunction(int&& val); //what does this mean?!
I understand the idea of passing-by-reference, so
void addTwo(int& a)
{
a += 2;
}
int main()
{
int x = 5;
addTwo(x);
return 0;
}
works and is intuitive to me.
This is not a reference of a reference, but rather a new language feature called an rvalue reference that represents (informally) a reference to an object in memory that isn't referenced elsewhere in the program and can be destructively modified. For example, the return value of a function can be captured by an rvalue reference, as can temporary values introduced into expressions.
Rvalue references can be used for a variety of purposes. From the perspective of most C++ programmers, they can be used to implement move semantics, whereby a new object can be initialized by "moving" the contents of an old object out of the old object and into a new object. You can use this to return huge objects from functions in C++11 without paying a huge cost to copy the object, since the object used to capture the return value can be initialized using the move constructor by just stealing the internals from the temporary object created by the return statement.
Move semantics are orthogonal to copy semantics, so objects can be movable without being copyable. For example, std::ofstreams are not copyable, but they will be movable, so you could return std::ofstreams from functions using the move behavior. This currently cannot be done in C++03. For example, this code is illegal in C++03 but perfectly fine (and encouraged!) in C++11:
std::ifstream GetUserFile() {
while (true) {
std::cout << "Enter filename: ";
std::string filename;
std::getline(std::cin, filename);
ifstream input(filename); // Note: No .c_str() either!
if (input) return input;
std::cout << "Sorry, I couldn't open that file." << std::endl;
}
}
std::ifstream file = GetUserFile(); // Okay, move stream out of the function.
Intuitively, a function that takes an rvalue reference is a function that (probably) is trying to avoid an expensive copy by moving the contents of an old object into a new object. For example, you could define a move constructor for a vector-like object by having that constructor take in an rvalue reference. If we represent the vector as a triple of a pointer to an array, the capacity of the array, and the used space, we might implement its move constructor as follows:
vector::vector(vector&& rhs) {
/* Steal resources from rhs. */
elems = rhs.elems;
size = rhs.size;
capacity = rhs.capacity;
/* Destructively modify rhs to avoid having two objects sharing
* an underlying array.
*/
rhs.elems = nullptr; // Note use of nullptr instead of NULL
rhs.size = 0;
rhs.capacity = 0;
}
It's important to notice that when we clear out rhs at the end of the constructor that we end up putting rhs into such a state that
Will not cause a crash when its destructor invokes (notice that we set its element pointer to nullptr, since freeing nullptr is safe), and
Still lets the object be assigned a new value. This latter point is tricky, but it's important to ensure that you can still give the cleared-out object a new value at some point. This is because it is possible to obtain an rvalue reference to an object that can still be referenced later in the program.
To shed some light on (2), one interesting use case for rvalue references is the ability to explicitly move values around between objects. For example, consider this idiomatic implementation of swap:
template <typename T> void swap(T& lhs, T& rhs) {
T temp = lhs;
lhs = rhs;
rhs = temp;
}
This code is legal, but it's a bit unusual. In particular, it ends up making three copies - first when setting temp equal to a copy of lhs, once setting lhs to be a copy of rhs, and once setting rhs to be a copy of temp. But we don't really want to be making any copies at all here; instead, we just want to shuffle the values around. Consequently, in C++11, you'll be able to explicitly get rvalue references to objects by using the std::move function:
template <typename T> void swap(T& lhs, T& rhs) {
T temp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(temp);
}
Now, no copies are made at all. We move the contents of lhs into temp, then move the contents of rhs into lhs, then moves the contents of temp into rhs. In doing so, we left both lhs and rhs in an "emptied" state temporarily before putting new values into them. It's important that when writing the code to move the contents out of an object that we leave the object in a somewhat well-formed state so that this code works correctly.
It's not a reference to a reference. It's a new syntax introduced in C++0x for so-called Rvalue references.