Monday, September 08, 2014

std::move makes things f**kable

After spending sometime figuring out C++ 11 move semantics,
I wanted to write some semi-formal intuitions about it,
for anyone encountering bugs related to it.

(Google will bring you to more precise, thought-out descriptions than this one for sure)

The most important thing:
std::move(A) does not actually move A! it just tells the compiler - from now on
you can fuck with A.

How does it do this?
It turns A into the most insignificant type of creature in the world of C++.
The type of creature that does not even have a name...
This creature is called - an rvalue. r stands for `right' - not as in correct , but as in the opposite of left.

What are rvalues?
Think of the expression
i = 5;

any programmer is aware that i is something that exists, there  is a place in the memory
that the computer calls i, and that place can store a value.

But what about the 5 on the right hand side?
where was it before it got put into i?
It was in some temporary place in the memory that does not even have a name.
A place that the computer feels free to treat like shit.
Will the 5 still be there after it is put into i?
Nobody gives a fuck.
Nobody's looking out for that place.

This is what it's like being an rvalue.

Sadly, in the world it sometimes pays off to treat people, or variables, like shit.

Suppose you had an expression

A=B;

The compiler would probably decide this means it has to copy all the data stored in B
and store this copy in A.

Suppose we don't care that much about what will happen to B after running this line.
We can tell the compiler - feel free to ruin B while running this line if it pays off.
e.g. if B contains a pointer to some large object, it would be very efficient
to have A point to the same object, and set B's pointer to null, rather than making
a copy of the large object.

writing
A= std::move(B);
will encourage the compiler  to do this.

How does this actually happen?

I like to think of it like this.
Suppose you have a prison named `operator ='

In this prison the are 3 wards -
One for the rich prisoners where they can be sure nothing bad will happen to them.
One for the regular prisoners where, bad things may happen to them, but no one will intentionally
try to screw them over,
and one for the prisoners someone had it in for, and wanted to make sure they would really get screwed.

The entrances of the wards look in C++, respectively, like this

1.operator=(const object& B)
2.operator=(object& B)
3.operator=(object&& B)


The double && is a way to tell C++ - B is an insignificant little worm! (that is, an rvalue). Feel free to tear him apart while running this operator!

The well-known `const' on the other extreme is a way of saying -
don't you dare touch B!

using move can become crucial when dealing with the specie in C++ 11 that's on top of the food chain - the guy nobody can touch.. the Unique Pointer.

A unique pointer in a way, is even tougher than the untouchable const object.
As the name suggests, you cannot even make a copy of a unique pointer.
(This turns out to be extremely useful for preventing memory leaks)

The big mess is that, not only the unique pointer itself, but consequently any thing
containing a unique pointer cannot be messed with. For example, a vector of unique pointers
to String.

For unique pointers, std::move is a way for the gang leader (i.e. the programmer)
to tell the gang's representative in the prison (i.e. the compiler)
`This guy no longer has my protection, you can make him your bitch'

The thing is, the gang representative will hesitate about messing with the unique pointer
if he is not constantly reassured by the gang leader that the u.p really does not have his protection any more.
This hesitation will cause compiling errors.

For example,
Suppose you have a method containing a line
A=B;
where B is a u.p the method got as a parameter.
You may think if I call the method with std::move(B) everything will be fine.
(cause a u.p cannot be copied, but it can be moved)
but that is not enough!Cause by the time you get into the method the compiler will hesitate
whether he can really mess with B,
and you will have to reassure him by writing again
A=std::move(B);

This is worst when calling constructors, where for reasons I do not fully understnad
you need to use std::move(B) in the initializer list - by the time you reach the
start of the method it's too late to mess with B. (well this last part is not completely correct,
but I'm tired and it's late)

No comments:

Blog Archive