/**
* This example is all about lvalue vs. rvalues. This is an important topic,
* especially in the context of C++11 which contains rvalue references. That's
* a bigger topic for later.
*
* lvalue's:
* - An object that persists beyond a single expression [1].
* - An object that has a name [1].
*
* rvalue's:
* - Can evaluate to a temporary value [1].
* - Do NOT represent an object occupying an identifiable location in memory [2].
*
* I've enclosed parts of the program that do not compile inside of error
* macros. You can uncomment one at a time to see what happens when we use
* non-modifiable lvalues or assign to rvalues.
*/
#include <iostream>
//#define ERROR
//#define ERROR2
//#define ERROR3
//#define ERROR4
//#define ERROR5
//#define ERROR6
//#define ERROR7
//#define ERROR8
struct S1;
/**
* This is a struct that will be a non-modifiable lvalue when created. Just
* come back to this after you get down a ways.
*/
struct S2
{
int value1;
#if defined ERROR5
double const value2; //< This is what makes it non-modifyable.
#endif
double value3;
S2()
: value1(0)
#if defined ERROR5
, value2(13.4)
#endif
, value3(7)
{
}
};
int main(int, char**)
{
int magicNumber;
// Let's first consider assignment. Assignment must have a left hand
// side with an lvalue, and a right hand side that is an rvalue:
magicNumber = 12234;
// magicNumber is an lvalue, it persists to the end of main AND it
// has a name.
// 12234 is an rvalue, it only exists inside the context of the
// assignment.
// With assignment it is simple to see the conflict if we treated
// rvalues as lvalues:
#if defined ERROR
12234 = magicNumber;
// Clearly this is an error! A number is not a variable or a place that
// can store anything. It is the most basic rvalue.
#endif
// I started to feel comfortable with this lvalue/rvalue stuff until
// I encountered:
int const myConstantMagicNumber = 13;
// I thought to myself, this is clearly an rvalue- you can't put it on the
// left hand side of an assignment. I wouldn't have cared much about this
// except the documentation several places makes the distinction between
// "modifiable" lvalues and "non-modifiable" lvalues:
// Non-Modifiable lvalues include [3]:
//// - Array Types.
#if defined ERROR2
// The following yields an error where it states:
// "invalid array assignment", verified this.
int myNums1[13];
int myNums2[13];
myNums1 = myNums2;
#endif
//// - Incomplete Types.
#if defined ERROR3
S1* s1 = 0;
S1* s2 = 0;
// This is obviously not good. Produces:
// "invalid use of incomplete type S1"
*s1 = *s2;
#elif !defined ERROR5
// This is only for demonstrative purposes (and provided that ERROR5 is
// also not enabled).
S2* s2_3 = new S2();
S2* s2_4 = new S2();
// This is the same above, except the class is defined!
*s2_3 = *s2_4;
delete s2_3;
delete s2_4;
#endif
//// - Const-Qualified Types
#if defined ERROR4
// Produces error: "assignment of read only value"
int const s1 = 15;
s1 = 18;
#endif
//// - Object with Structure or Union Type and one member is a const-qualified
//// type.
#if defined ERROR5
// Produces: "synthesized method 'S2& S2::operator=(S2 const&)' first required here"
S2 s1;
S2 s2;
s1 = s2;
#else
// We can safely perform the copy because all of the members of S2
// are non-const and the operator= can safely be generated.
S2 s2_1;
S2 s2_2;
s2_1 = s2_2;
#endif
// It's easy to see how we can get confused between modifiable lvalues, non-modifiable
// lvalues, and rvalues. Each of the four examples above shows an lvalue (named and
// they persist) but are NOT modifiable. Here are some other cool examples of lvalues
// that are modifiable:
int* myValue = &magicNumber;
*myValue; // The pointer to myValue is a modifiable lvalue.
// This is by far the coolest example of lvalues around. It uses the ternary operator
// but does indeed evaluate to an lvalue! [4]
int a = 0;
int b = 0;
(magicNumber < 10 ? a : b) = 17;
std::cout << "a = " << a << ", b = " << b << std::endl;
// You should check out the table in reference [3] about certain operators and whether
// or not they work on lvalue's or rvalue's:
//// operator& - Operator must be an lvalue
#if defined ERROR6
int* addressOfSomething = &7 //< What is an address of 7?
#else
int value = 7;
int* addressOfSomething = &value; //< Requires lvalue!
#endif
//// operator++, operator-- - Operator must be an lvalue.
#if defined ERROR7
++7; //< Failure for both postfix and prefix, they modify a storage
13++; //< value.
#else
int anotherValue = 7;
anotherValue++;
++anotherValue;
#endif
//// =, +=, -=, *=, %=, <<=, >>=, &=, ^==, |= - Left hand side must be lvalue.
#if defined ERROR8
7 += 9; //< Cannot store back into an rvalue!
#else
int yetAnotherValue = 7; //< Fine because we are modifying an lvalue.
yetAnotherValue += 9;
#endif
}
/**
* In conclusion this is a pretty boring topic but well worth knowing. The subtle
* nuances between lvalues and rvalues are a good thing to know going into learning
* about C++11 and the rvalue references. Sorry for the boring entry today!
*/
/**
REFERENCES
[1] - http://msdn.microsoft.com/en-us/library/f90831hc.aspx
[2] - http://eli.thegreenplace.net/2011/12/15/understanding-lvalues-and-rvalues-in-c-and-c
[3] - http://publib.boulder.ibm.com/infocenter/comphelp/v7v91/index.jsp
[4] - http://en.wikipedia.org/wiki/%3F:
*/
No comments:
Post a Comment