1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
|
Although this section contains forward references to chapters ref(String),
ref(Classes), and ref(PointMembers), its topic best fits the current
chapter. This section can be skipped without loss of continuity, and you might
consider returning to it once you're familiar with the contents of these
future chapters.
Historically, the bf(C) programming language distinguished between
em(lvalues) and em(rvalues). The terminology was based on
assignment expressions, where the value to the left of the assignment operator
receives a value (e.g., it referred to a location in memory where a value
could be written into, like a variable), while the value to the right of the
assignment operator only had to represent a value (it could be a temporary
variable, a constant value or the value stored in a variable):
verb(
lvalue = rvalue;
)
bf(C++) adds to this basic distinction several new ways of referring to
values:
itemization(
itt(lvalue): an emi(lvalue) in bf(C++) has the same meaning as in
bf(C). It refers to a location where a value can be stored, like a
variable, a reference to a variable, or a dereferenced pointer.
itt(xvalue): an emi(xvalue) indicates an emi(expiring value). An expiring
value refers to an em(object) (cf. chapter ref(Classes)) just before
its lifetime ends. Such objects normally have to make sure that
resources they own (like dynamically allocated memory) also cease to
exist, but such resources may, just before the object's lifetime ends,
be moved to another location, thus preventing their destruction.
itt(gvalue): a emi(gvalue) is a emi(generalized lvalue). A generalized
lvalue refers to anything that may receive a value. It is either an
lvalue or an xvalue.
itt(prvalue): a emi(prvalue) is a emi(pure rvalue): a literal value (like
tt(1.2e3)) or an immutable object (e.g., the value returned from a
function returning a constant tt(std::string) (cf. chapter
ref(String)))
)
An expression's value is an xvalue if it is:
itemization(
it() the value returned by a function returning an rvalue reference to an
object;
it() an object that is cast to an rvalue reference;
it() an expression accessing a non-static class data member whose object
is
itemization(
it() an xvalue, or
it() a tt(.*) (pointer-to-member) expression (cf. chapter
ref(PointMembers)) in which the left-hand side operand is an
xvalue and the right-hand side operand is a pointer to a data
member.
)
The effect of this rule is that named rvalue references are treated as
lvalues and anonymous rvalue references to objects are treated as
xvalues.nl()
Rvalue references to functions are treated as lvalues whether
anonymous or not.
)
Here is a small example. Consider this simple struct:
verb(
struct Demo
{
int d_value;
};
)
In addition we have these function declarations and definitions:
verb(
Demo &&operator+(Demo const &lhs, Demo const &rhs);
Demo &&factory();
Demo demo;
Demo &&rref = static_cast<Demo &&>(demo);
)
Expressions like
verb(
factory();
factory().d_value;
static_cast<Demo &&>(demo);
demo + demo
)
are xvalues. However, the expression
verb(
rref;
)
is an lvalue.
In many situations it's not particularly important to know what kind of gvalue
or what kind of rvalue is actually used. In the annotations() the term
emi(lhs) (i(left hand side)) is frequently used to indicate an operand that's
written to the left of a binary operator, while the term emi(rhs)
(i(right hand side)) is frequently used to indicate an operand that's
written to the right of a binary operator. Lhs and rhs operands could actually
be gvalues (e.g., when representing ordinary variables), but they could also
be prvalues (e.g., numeric values added together using the addition
operator). Whether or not lhs and rhs operands are glvalues can always be
determined from the context in which they are used.
|