Lvalue - Rvalue distinctions
Lvalue or location value is an address that is stored into. In the assignment
a = 3;
we have the lvalue of a
used on the
left hand side and the integer contant value 3 assigned to this location. In
the assignment
b = a;
we have the rvalue of a
(3) used to
assign to the lvalue of b
. So depending on which side of an assignment,
we use the variable a
, we get two different behaviors. In declarations,
we can see a difference between lvalue and rvalue in the following declarations:
int dta[3] = {1, 2, 3};
int i = data[0];
int* p = &data[1];
int& last = data[2];
In the first declaration, the array's three locations
(lvalues) are initialized to the respective constants. The variable i
is initialized to the rvalue of data[0]
. The pointer p
is initialized to the lvalue of data[1]
, in other words, the address
of the array element data[1]
. The variable last is a reference
initialized to the lvalue of data[2]
. The pointer p
is for the moment another alias for getting to data[1]
, but p
can have its contents changed to point to some other location, whereas the alias
last cannot be changed within the scope of these declarations. Notice that while
both pointer p
and reference declaration last
both
allow you to get at a specific data element, the pointer p
itself
can be changed so that it can look at other elements of the array; for example,
p
can also be set to look at the third element of the array by using
the statement
p = &data[2];
In contrast, the reference declaration last
provides an alias or additional name for the array element data[2]
. By definition, variables delcared with the type&
form cannot
be modified. They are constant pointers. Thus, the variable last
cannot be changed and will always point at the array element data[2]
, though the contents of data[2]
can be changed.
Let's use this in the context of class members. We
will have return types that are rvalue types, and return types that are lvalue
types. In the following example, in the member function double& x_lv()
the return type is an lvalue, so return x
must return the location
of x
. In the member function double get_x()
the return
type is a double
, so return x
uses the rvalue which
is copied back as the value of this function.
#include < iostream >
#include < string >
using namespace std;
class point {
public:
point(double x_in = 0.0, double y_in = 0.0): x(x_in), y(y_in
{}
double get_x() { return x;} //returns the rvalue of x
double get_y() { return y;}
double& x_lv() { return x;} //returns an alias to x
double& y_lv() { return y;}
private:
double x, y;
};
int main()
{
point a(2.5, -2.5), b(1.0, 1.0), c;
cout << "lvalue-rvalue semantics\n";
//compute the midpoint
c.x_lv() = (a.get_x() + b.get_x()) / 2;
c.y_lv() = (a.get_y() + b.get_y()) / 2;
//notice you get the same output because
//an lvalue is automatically dereferenced where
//appropriate to give the rvalue
cout << c.get_x() << ", " << c.get_y() << endl;
cout << c.x_lv() << ", " << c.y_lv() << endl;
}