int i = 1;
int* p = &i; // address exists!
i = 2; // memory is modified!
class foo;
foo f; // also resides in memory!
int x = 2;
int* p = &(x+2); // error! not a memory location!
i+2 = 4; // error! cannot assign to rvalue!
auto sum = [](int x, int y){return x + y;};
sum(3,4) = 2; // error! rvalue!
Foo f;
f = foo(); // foo() is an rvalue!
The “l” stands for “left hand side”, and “r” is “right hand side”.
The classic reference type is denoted by {type}&
, like int& x = y
.
We cannot assign a reference an rvalue!
int x = 3;
int& y = x; // OK!
int& z = 2; // ERROR!
There is a small exception, which is constant references!
int x = 3;
int& y = 2; // ERROR!
const int& z = 2; // OK!
Why does this work? during compile time, you declare something to be const
allows the right hand side to exist as an lvalue temporarily, so then the reference can capture it properly.
You would think that most functions just return by value, giving you all rvalues.
That’s not true.
Consider the following:
int foo(){ return 3; }
int global = 3;
int& bar(){ return global; }
Here, foo()
returns an rvalue. However, bar()
returns an lvalue.
We know int& x
as a reference. In C++11, we introduce the idea of an rvalue reference, denoted by int&& x
.
void foo(int& i){ ... }
void foo(int&& i){ ... }
int a = 1;
foo(a); // calls (int& i)
foo(5); // calls (int&& i)
As we said above, you can have two different arguments. However, can we add this?
void foo(int i){ ... }
void foo(int& i){ ... }
void foo(int&& i){ ... }
int a = 1;
foo(a); // ERROR!
The answer is no. It’s extremely ambiguous, because you can pass by value or pass by reference in general.
In CS32, I’ve learned the idea of a constructor and a copy constructor. However, there is a third type in c++11: move constructor.
A move constructor takes in an rvalue, instead of a const reference or a bunch of inputs:
class Foo{
public:
...
Foo(const Foo&& rhs){ // move ctor
...
}
}
This constructor takes in an rvalue, which is a temporary object residing on the registers(not in memory!)
So now that you’ve seen it, how do you use it?
std::move
We use std::move
to invoke the move constructor of the object. An example:
void bar(Foo&& foo){
...
}
bar(std::move(foo)); // works!
A general survey can be shown as:
bar_by_ref(foo); // no constructor calls
bar_by_val(foo); // calls copy constructor
bar_by_move(std::move(foo)); // calls move constructor
So as you can see the ranking of speed is : reference > move > value. Generally.
std::move
efficiently?If you wanted to move an lvalue, you would likely have to use an RAII container that does this for you. Refer to the Essential C++ blog for RAII.
std::auto_ptr<Foo> foo(new Foo()); // auto_ptrs are deprecated btw
bar(std::move(foo)); // changed ownership. foo now is null.
If you wanted to move an rvalue, you’re in luck! You can std::move()
as much as you want.
In fact, in C++11 and above, the stl containers implemented std::move
internally for rvalues(not sure about lvalues), and the performance went up by a noticeable margin.
If you’ve got a large object that you want to std::move
, aka swap pointers with large memory members, then implement your own move constructor!