Definition:
Lets first get a one line introduction and study each of the different categories of expressions taxonomy intimately in the subsequent sections.
L-Value
It’s traditionally called so because it usually appears on the left side of an assignment expression. It’s usually associated with a particular object.
R-Value
It’s traditionally called so because it usually appears on the right side of an assignment expression. It’s either a temporary(X-Value) or pure(R-Value) and it’s not associated with any object.
X-Value
It’s called an eXpiring value. It refers to an object nearing the end of its lifetime.X-Values are what makes move-semantics possible.
GL-Value
GL-Value is either L-Value or an X-Value. It’s called “generalised L-Value”.
PR-Value
PR-Value is called “pure” R-Value. It’s an R-Value that is not an X-Value. For example, if you call a function which is not a “reference”, it’s a pure R-Value.
Every value of an expression in C/C++ falls into one of these categories. This
Making sense of these
L-Value
Let’s go with the simplest one first, the L-Value! ANY “named” expression is an L-Value. In the following code snippet, all of the variables declared are L-Values.
struct Obj
{
int one = 100;
float two = 20.f;
};
Obj&& funcReturningRValueRef()
{
return std::move(Obj());
}
//All of the implementations below cover to an L-Value.
Obj lValue;
Obj& lValueToo = lValue;
Obj&& alsoLValue = std::move(lValue);
Obj&& thisIsLvalueToo = funcReturningRValueRef();
X-Value
X-Values are what makes move-semantics possible. They enable us to access memory of an object that’s nearing the end of its lifetime(eg., local variable going out of scope) and perform move operations on the memory pointed. X-Values are created by following types of expressions:
– Function calls that return R-Value references to objects.
– When casting to R-Value references to Objects.
– When accessing members of a class through an object that’s an X-Value.
– When accessing members through a (.*) pointer-to-member expression in which first operand is an X-Value and second operand is a pointer to data member.
//First Case
Obj&& funcReturningRValueRef(); // This returns an X-value
//Second Case
Obj testObj;
static_cast<Obj&&>(testObj); // This is an X-Value.
Obj&& NotRValue = static_cast<Obj&&>(testObj); // NotRValue is L-Value here
//Third Case
funcReturningRValueRef().one = 10; // Compile Error! <.one> is an X-Value
//Fourth Case
int Obj::*ptrToMember = &Obj::one; //Pointer to class member
funcReturningRValueRef().*ptrToMember = 10; //Compile Error! Function returns R-Value
GL-Value, PR-Value
A GL-Value could be an L-Value or and X-Value. This generalization was necessary to differentiate between values that associate with a particular object and values that do not. When we say R-Value from C++0x, we usually mean R-Value references(which hold a reference to an object in memory, though temporary). Therefore, at any given point in time, a GL value will always associate with an object, while PR-Value is does not.
If GL-Value appears in context where PR-Value is expected, it’s implicitly converted into a PR-Value.
int x = 23; //23 here is a PR-Value
struct Test{
int func()
{
//glvalue-to-prvalue conversion is applied here
return m;
}
int m = 10;
}
------------------------
Test test;
test.func(); // This function returns a PR-Value
------------------------
Test first;
Test second;
// First and Second are both GL-Values
// to first.m
second = first;
void operator=(const Test& other)
{
//All of the members of the assignee basically can basically used as R-Values
this->m = other.m;
}
------------------------