 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Ben Hutchings Guest
|
Posted: Thu Oct 16, 2003 8:47 am Post subject: Defining lvalue, rvalue, pointer and reference |
|
|
Following the thread "Fancy pointers that behave like Java-style
reference?", here's my attempt to define what these things are and
how they are related. They aren't actually that simple, but
hopefully they avoid using vague terms such as 'alias' and are also
correct.
Lvalue and rvalue:
Every expression can be statically determined to yield either an
lvalue or an rvalue. An lvalue has an object, function, reference-
to-object or reference-to-function type and potentially addresses
an object or function. An rvalue has an object type and is
potentially a temporary value. Evaluating an expression may
result in undefined behaviour and thus not yield anything
meaningful, hence the key word 'potentially'.
Reference and pointer:
A reference is an lvalue that is initialised to address either the
same object or function as another lvalue or a new object
initialised from an rvalue. In the latter case the new object and
has a const-qualified type and the reference has a reference-to-
const type. [Explanation of the lifetime of the object omitted.]
A pointer is a value that is potentially the address of an object,
function, or region of storage, but it is not necessarily a valid
address. An lvalue of pointer type (potentially) addresses such
a value and not any entity that is (potentially) addressed by the
pointer itself.
Relations between the above:
A lvalue that addresses a non-const object can generally be used
to assign an rvalue of the corresponding type to that object, but
this can be disabled by class-types.
An lvalue that addresses an object can be converted to an rvalue
if the object has been initialised or assigned to [are there any
exceptions to this?]. The type of the rvalue is the
corresponding object type stripped of any cv-qualification and
its value is the current value of the object. An lvalue that
addresses a function can be converted to an rvalue of the
corresponding pointer-to-function type.
The unary '&' and '*' operators convert between rvalues of
pointer-to-object and pointer-to-function types and lvalues of
the corresponding object and function types.
[Explanation of ill-formed and undefined cases omitted.]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Ben Hutchings Guest
|
Posted: Thu Oct 16, 2003 8:07 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
Despite spending a lot of time writing this, I later recognised some
errors in it. Before everyone jumps on me, here are my own
revisions:
I wrote:
| Quote: | Every expression can be statically determined to yield either an
lvalue or an rvalue.
|
Every well-formed expression can be determined at translation time to
be either an lvalue or an rvalue.
[Current standards says "is" not "yields".]
| Quote: | An lvalue has an object, function, reference-to-object or
reference-to-function type...
|
An lvalue has a type other than void...
[The original is overly complex, yet also omits member-function
types.]
| Quote: | An rvalue has an object type and is potentially a temporary
value.
|
An rvalue has an object or void type and potentially yields a
temporary value or (in the case of void) a lack of value.
| Quote: | A reference is an lvalue that is initialised to address either the
same object or function as another lvalue or a new object
initialised from an rvalue.
|
A reference addresses an object or function. It can be initialised
with an lvalue of the same type or of the type it refers to. If it
is a reference to a const object type, it can be initialised with
an rvalue of the unqualified object type.
[An lvalue is a kind of expression, so a reference can't be an
lvalue.]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
johnchx Guest
|
Posted: Sun Oct 19, 2003 7:25 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
[email]do-not-spam-benh (AT) bwsint (DOT) com[/email] (Ben Hutchings) wrote
| Quote: | Following the thread "Fancy pointers that behave like Java-style
reference?", here's my attempt to define what these things are and
how they are related. They aren't actually that simple, but
hopefully they avoid using vague terms such as 'alias' and are also
correct.
|
A good idea indeed...so good I feel compelled to take a stab at it myself. :-)
Expressions
-----------
-> every expression has a type
-> every expression is an lvalue or an rvalue (3.10/1)
-> the language provides rules which determine
whether an expression is an lvalue or an rvalue
Reference Type
--------------
-> an expression with reference type is interpreted as an
lvalue, regardless of the other rules which might apply
-> having reference type has no further effect on the
interpretation of an expression ( 5/6 )
References
----------
-> the phrase "a reference" may refer to one of the
following: a non-member name with reference type,
a member name with reference type, the value of a
function whose declared return type has reference
type, or a function parameter of reference type
-> it is possible to declare a non-member name as having
reference type; the definition of such a name shall
initialize it with an lvalue of compatible type
-> it is possible to declare a member name as having
reference type; such a member must be initialized
with an lvalue of compatible type
-> it is possible to declare a function as returning a
reference type; a function call expression calling
such a function is an lvalue; the function shall
be defined to return an lvalue
-> it is possible to declare a function parameter of
reference type; a function call expression calling
such a function must initialize such a parameter with
an lvalue of compatible type.
lvalues & rvalues
-----------------
-> the concepts of type an lvalue/rvalue-ness are
completely orthogonal, with two exceptions:
(a) it is impossible to have an rvalue of
function type and
(b) all expressions with reference type are
lvalues
-> put another way, the purpose of reference type
is to allow the programmer to use the type system
to affect the lvalue-ness of expressions
-> intuitively, an lvalue denotes a location, while
an rvalue denotes a value.
-> an lvalue-to-rvalue conversion is accomplished by
reading the value stored at the location denoted
by the lvalue (this is not allowed for lvalues
of function type)
-> an rvalue-to-lvalue "conversion" can in general
be accomplished only by copying the value
denoted by the rvalue into a known location,
which is thereafter denoted by the lvalue; this
is called "introducing a temporary"
-> it is possible to take the address of an lvalue;
it is not possible to take the address of an
rvalue
Pointers
--------
-> if p has the type pointer-to-T, the expression *p is
an lvalue of type T (5.3.1/1); it does not have
reference type
Comments and corrections welcome, of course!
-- John
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
thp@cs.ucr.edu Guest
|
Posted: Mon Oct 20, 2003 1:47 am Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
johnchx <johnchx2 (AT) yahoo (DOT) com> wrote:
+ [email]do-not-spam-benh (AT) bwsint (DOT) com[/email] (Ben Hutchings) wrote
+
+> Following the thread "Fancy pointers that behave like Java-style
+> reference?", here's my attempt to define what these things are and
+> how they are related. They aren't actually that simple, but
+> hopefully they avoid using vague terms such as 'alias' and are also
+> correct.
+
+ A good idea indeed...so good I feel compelled to take a stab at it myself.
+
+
+ Expressions
+ -----------
+ -> every expression has a type
+
+ -> every expression is an lvalue or an rvalue (3.10/1)
+
+ -> the language provides rules which determine
+ whether an expression is an lvalue or an rvalue
+
+
+ Reference Type
+ --------------
+ -> an expression with reference type is interpreted as an
+ lvalue, regardless of the other rules which might apply
+
+ -> having reference type has no further effect on the
+ interpretation of an expression ( 5/6 )
+
+
+ References
+ ----------
+ -> the phrase "a reference" may refer to one of the
+ following: a non-member name with reference type,
+ a member name with reference type, the value of a
+ function whose declared return type has reference
+ type, or a function parameter of reference type
+
+ -> it is possible to declare a non-member name as having
+ reference type; the definition of such a name shall
+ initialize it with an lvalue of compatible type
+
+ -> it is possible to declare a member name as having
+ reference type; such a member must be initialized
+ with an lvalue of compatible type
+
+ -> it is possible to declare a function as returning a
+ reference type; a function call expression calling
+ such a function is an lvalue; the function shall
+ be defined to return an lvalue
+
+ -> it is possible to declare a function parameter of
+ reference type; a function call expression calling
+ such a function must initialize such a parameter with
+ an lvalue of compatible type.
+ lvalues & rvalues
+ -----------------
+ -> the concepts of type an lvalue/rvalue-ness are
+ completely orthogonal, with two exceptions:
+
+ (a) it is impossible to have an rvalue of
+ function type and
+ (b) all expressions with reference type are
+ lvalues
+
+ -> put another way, the purpose of reference type
+ is to allow the programmer to use the type system
+ to affect the lvalue-ness of expressions
+
+ -> intuitively, an lvalue denotes a location, while
+ an rvalue denotes a value.
+
+ -> an lvalue-to-rvalue conversion is accomplished by
+ reading the value stored at the location denoted
+ by the lvalue (this is not allowed for lvalues
+ of function type)
+
+ -> an rvalue-to-lvalue "conversion" can in general
+ be accomplished only by copying the value
+ denoted by the rvalue into a known location,
+ which is thereafter denoted by the lvalue; this
+ is called "introducing a temporary"
+
+ -> it is possible to take the address of an lvalue;
+ it is not possible to take the address of an
+ rvalue
+
+
+ Pointers
+ --------
+ -> if p has the type pointer-to-T, the expression *p is
+ an lvalue of type T (5.3.1/1); it does not have
+ reference type
+
+
+ Comments and corrections welcome, of course!
1) Nice work.
2) You omitted initialization of const references via rvalues.
3) I don't understand what you mean by "the concepts of type an
lvalue/rvalue-ness are completely orthogonal ..." By definiton,
they are complements of each other, in the sense that every
expression is one or the other but never both.
4) In the clause:
intuitively, an lvalue denotes a location, while
an rvalue denotes a value.
I presume that "location" means roughly the same thing as
"object". It might be worthwhile mentioning that some
lvalues, e.g., "*(int*)0" don't represent objects, while
some rvalues denote objects, e.g., "f()" where f returns
a value of a user-defined type.
5) In the clause:
an expression with reference type is interpreted as an
lvalue, regardless of the other rules which might apply
I'd drop "interpreted as" and add "denoting the referent
of the corresponding reference. It may undergo subsequent
lvalue-to-rvalue conversion."
6) The fact that all post-initialization occurrences of refernces
denote the referent makes it impossible directly to reseat
a reference and/or determine its location. To prohibit doing
either of those things via indirection:
- Pointers, reference, and arrays to/of references are not
permitted.
- Struct-or-class objects having reference members have
no layout rules.
7) By definition, sizeof(T&) denotes sizeof(T).
Tom Payne
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
johnchx Guest
|
Posted: Mon Oct 20, 2003 6:01 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
[email]thp (AT) cs (DOT) ucr.edu[/email] wrote
Thanks, and thanks for the comments as well.
| Quote: | 2) You omitted initialization of const references via rvalues.
|
Well, they're in there if you know where to look. They're the
"rvalue-to-lvalue 'conversion'" I refered to. (At least that's one
way an rvalue-to-lvalue "conversion" can occur.)
I did leave out the special rule demanding that references initialized
this way be of const qualified type, and a number of other
reference-related rules (e.g. lifetimes of temporaries bound to
references). I chose to do that only because I was trying to find a
clean way to present the idea of references -- and directly related
ideas like lvalue -- rather than a comprehensive treatment of the
related topics. And I think the "const rule" has more to do with the
error-prone nature of the implicit conversions inherited from C than
with the essential ideas of references. (IIRC, the "const rule"
didn't exist at all in the primordial soup of C-with-classes, at least
in the early days.)
| Quote: | 3) I don't understand what you mean by "the concepts of type an
lvalue/rvalue-ness are completely orthogonal ..." By definiton,
they are complements of each other, in the sense that every
expression is one or the other but never both.
|
That should read "the concepts of type AND lvalue/rvalue-ness are
completely orthogonal ... " That is, every expression (a) has a type
and (b) has an lvalue/rvalue-ness, and (a) is orthogonal to (b), with
certain exceptions.
I put it this way to set up the notion that the purpose of reference
type is to allow one of these dimensions (type) to affect the other
(lvalue/rvalue-ness).
BTW, there's one more "non-orthogonality": you can't have an rvalue of
array type. I left that out originally.
| Quote: | 4) In the clause:
intuitively, an lvalue denotes a location, while
an rvalue denotes a value.
I presume that "location" means roughly the same thing as
"object". It might be worthwhile mentioning that some
lvalues, e.g., "*(int*)0" don't represent objects, while
some rvalues denote objects, e.g., "f()" where f returns
a value of a user-defined type.
|
I stand by my phrasing here -- in particular, location does *not* mean
the same as object, even roughly. (While every object is a region of
storage, not every region of storage is an object.) Lvalues may
denote functions, for example.
What I'm driving at here is the notion that there are two different
aspects of a referent -- its location and its value -- which may be
denoted by an expression, and lvalues denote the first aspect while
rvalues denote the second.
I've found this to be a useful intuitive guide, in the sense that when
I think about expressions this way, I can accurately "guess" what the
standard will say, and I can explain things about the standard that
would otherwise be mysterious (e.g. when does the function - to -
pointer-to-function conversion occur?).
| Quote: | 5) In the clause:
an expression with reference type is interpreted as an
lvalue, regardless of the other rules which might apply
I'd drop "interpreted as"
|
Well...I'm trying to draw attention to the idea that having reference
type changes the interpretation of an expression which, if it didn't
have reference type, would have been an rvalue.
| Quote: | and add "denoting the referent
of the corresponding reference.
|
All expressions denote their referents. lvalue/rvalue-ness and having
or lacking reference type doesn't change this. (I know that the
suggested language is more or less a quotation from the standard...I
suppose that the import of this phraseing is that the implementation
is prohibited from, e.g., copying the referent and treating the
expression as denoting the copy. So it probably does need to be in
the formal text, but I think it complicates the exposition.)
Another point to mention: I've tried to avoid mixing notion of
"reference type" with the terms "a reference" or "the reference."
There are (at least) four distinct language constructs that we
commonly refer to as "a reference," and I think that some of the
difficulty that we've seen in defining "a reference" comes from trying
to trying to find some phraseology that applies equally well to all
four constructs.
I think it is possible to give a single coherent account of the
meaning of reference type. I don't think "a reference" has a single,
unified definition and meaning.
| Quote: | It may undergo subsequent
lvalue-to-rvalue conversion."
|
This just follows from the general rules for building up a full
expression from sub-expressions, I think. In other words, it may
undergo any valid conversion; there's nothing special about the
lvalue-to-rvalue conversion in this context.
| Quote: | 6) The fact that all post-initialization occurrences of refernces
denote the referent makes it impossible directly to reseat
a reference and/or determine its location.
|
I think this is a confusing notion that arises from thinking about
names declared with reference type as "pointer-like." In fact,
there's nothing unusual at all about this. Consider:
int main() {
int i = 0;
int* pi = &i;
}
After its definition, "i" is an lvalue which denotes an object of type
int. How would you "reseat" it? How would you take its address? Not
the address of the denoted object, but of "i" itself?
"pi" is the same: after its definition, it is an lvalue denoting an
object of type int*. You can change the value stored there, but you
can't cause "pi" to denote some other object. What you *can* do is
"re-seat" the result of *pi. But again, there's no magic. Consider:
struct A {
int* mp1;
int* mp2;
void reseat( bool b ) { mb = b; }
int& foo() { return mb ? *mp1 : * mp2 ; }
bool mb;
A(int* p1, int* p2 ):
mp1(p1), mp2(p2), mb (true) {}
};
int i, j;
A a (&i, &j);
If I change the value of a.mb, I can change the result of a.foo(), an
expression with reference type. Have I "re-seated" a reference? Not
in any meaningful sense.
What I'm getting at here is that the quality of being "un-reseatable"
is in no way peculiar to expressions with reference type.
| Quote: | To prohibit doing
either of those things via indirection:
- Pointers, reference, and arrays to/of references are not
permitted.
|
I think it's confusing to view these as special prohibitions. They
simply follow from the definition of the compound types. Pointers are
pointers to void or to objects or functions of a given type.
Refereces are references to objects or functions of a given type.
Arrays are arrays of objects of a given type.
Saying that you can't have a pointer to "a reference" is, in my view,
a lot like saying that you can't have a pointer to a namespace -- the
idea is non-sensical.
| Quote: |
- Struct-or-class objects having reference members have
no layout rules.
|
Doesn't 9.2/12 apply?
| Quote: | 7) By definition, sizeof(T&) denotes sizeof(T).
|
Yes, though I think the only reason that there's a special rule on
this is that adjusting the type of an expression from T& to T might be
considered a step in the evaluation of the expression, and sizeof is
guaranteed not to evaluate its operand.
Thanks again for the thought-provoking comments!
-- John
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Marcin 'Qrczak' Kowalczyk Guest
|
Posted: Mon Oct 20, 2003 10:38 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
On Sun, 19 Oct 2003 19:25:19 +0000, johnchx wrote:
| Quote: | -> an expression with reference type is interpreted as an
lvalue, regardless of the other rules which might apply
|
What is the difference between T lvalue and T& lvalue, where T is any
non-reference type?
I mean: would anything change if we said that all lvalues have the
appropriate reference types? Then lvalueness and reference type would
always coincide, so we might start using only one term.
--
__("< Marcin Kowalczyk
__/ [email]qrczak (AT) knm (DOT) org.pl[/email]
^^ http://qrnik.knm.org.pl/~qrczak/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
David Abrahams Guest
|
Posted: Mon Oct 20, 2003 10:39 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
[email]thp (AT) cs (DOT) ucr.edu[/email] writes:
| Quote: | It might be worthwhile mentioning that some
lvalues, e.g., "*(int*)0" don't represent objects
|
Are you sure?
*(int*)0
is not even a valid expression.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Ben Hutchings Guest
|
Posted: Mon Oct 20, 2003 10:40 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
johnchx wrote:
| Quote: | do-not-spam-benh (AT) bwsint (DOT) com (Ben Hutchings) wrote
Following the thread "Fancy pointers that behave like Java-style
reference?", here's my attempt to define what these things are and
how they are related. They aren't actually that simple, but
hopefully they avoid using vague terms such as 'alias' and are also
correct.
A good idea indeed...so good I feel compelled to take a stab at it
myself.
snip |
I was expecting some specific criticism rather than a complete
alternative. I know there are errors even in my second article:
- I forgot to mention that references can't be bound to bitfields.
- I said that lvalues can have reference type, but this is not
correct; expressions apparently having reference type are
lvalues of the referred-to type. Reference types in
declarations are a way to require the use of lvalues rather
than rvalues.
- I forgot to mention the other special case of lvalue-to-rvalue
conversion: array-to-pointer.
What I was hoping was that it would be possible to come up with a
clearer explanation of these concepts and their relations than
there is in the current section 3.10, which would perhaps avoid
the differing (mis-)understandings apparent in the thread I
referred to (and others I have seen in comp.lang.c++.moderated).
As justification for this, I could point out some problems with
the current text:
Para 2: "An lvalue refers to an object or function." That
seems to mean that an lvalue is a reference, but technically
"reference" means something subtly different. It also has
the well-known problem of describing run-time behaviour
when lvalues are actually identified at translation time.
Para 2: "Some rvalue expressions...refer to objects." This
just confuses the issue rather than drawing a clear
distinction between the two.
Para 5. If lvalues and rvalues are expressions, then the
results of function calls cannot be lvalues or rvalues - only
the function call expressions can be.
Para 6: "An expression which holds a temporary object..." How
can an expression "hold" an object? Surely this should refer
to something like "A cast expression that potentially yields a
temporary object"?
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Gabriel Dos Reis Guest
|
Posted: Mon Oct 20, 2003 11:56 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
[email]qrczak (AT) knm (DOT) org.pl[/email] ("Marcin 'Qrczak' Kowalczyk") writes:
| Quote: | On Sun, 19 Oct 2003 19:25:19 +0000, johnchx wrote:
-> an expression with reference type is interpreted as an
lvalue, regardless of the other rules which might apply
What is the difference between T lvalue and T& lvalue, where T is any
non-reference type?
I mean: would anything change if we said that all lvalues have the
appropriate reference types?
|
Yes, I see a problem. If you equate lvalue with reference type then
what would be "T" in the following?
template<class T>
void sink(T) { }
int main()
{
int x = 9;
int* p = &x;
sink(*p); // *p is an lvalue. T = ?
}
| Quote: | Then lvalueness and reference type would
always coincide, so we might start using only one term.
|
--
Gabriel Dos Reis
[email]gdr (AT) integrable-solutions (DOT) net[/email]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Marcin 'Qrczak' Kowalczyk Guest
|
Posted: Tue Oct 21, 2003 1:50 am Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
On Mon, 20 Oct 2003 23:56:15 +0000, Gabriel Dos Reis wrote:
| Quote: | Yes, I see a problem. If you equate lvalue with reference type then what
would be "T" in the following?
|
int. Of course the language would work as today, only described
differently, so we would say that during template parameter deduction
if the parameter type is not a reference, then any toplevel reference of
the argument type is stripped.
In fact the description in the draft seems to assume that the type of the
argument can be a reference. It doesn't say that a reference parameter
type must be matched with an lvalue argument, it says that if both are
of the form T& then T can be deduced.
If it didn't change in the final standard, I'm afraid it's wrong. Either
argument types which would be references are turned into lvalues (and then
it's not possible to match T& parameter) or they remain references (and
then T parameter binds T to a reference type if the argument is a reference).
--
__("< Marcin Kowalczyk
__/ [email]qrczak (AT) knm (DOT) org.pl[/email]
^^ http://qrnik.knm.org.pl/~qrczak/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Gabriel Dos Reis Guest
|
Posted: Tue Oct 21, 2003 3:57 am Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
[email]dave (AT) boost-consulting (DOT) com[/email] (David Abrahams) writes:
| Quote: | thp (AT) cs (DOT) ucr.edu writes:
It might be worthwhile mentioning that some
lvalues, e.g., "*(int*)0" don't represent objects
Are you sure?
*(int*)0
is not even a valid expression.
|
Consider sizeof(*(int*)0).
--
Gabriel Dos Reis
[email]gdr (AT) integrable-solutions (DOT) net[/email]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
johnchx Guest
|
Posted: Tue Oct 21, 2003 6:16 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
[email]do-not-spam-benh (AT) bwsint (DOT) com[/email] (Ben Hutchings) wrote
| Quote: | johnchx wrote:
A good idea indeed...so good I feel compelled to take a stab at it
myself.
snip
I was expecting some specific criticism rather than a complete
alternative.
|
I know...but starting with a clean sheet is sometimes just too
tempting to resist. ;-)
[snip]
| Quote: | What I was hoping was that it would be possible to come up with a
clearer explanation of these concepts and their relations than
there is in the current section 3.10, which would perhaps avoid
the differing (mis-)understandings apparent in the thread I
referred to (and others I have seen in comp.lang.c++.moderated).
As justification for this, I could point out some problems with
the current text:
Para 2: "An lvalue refers to an object or function." That
seems to mean that an lvalue is a reference,
|
Reference types must be reference to an object type or reference to a
function type, and lvalues denote objects or functions. This isn't
just a coincidence, but it also doesn't imply that every lvalue has
reference type.
| Quote: | but technically
"reference" means something subtly different.
|
Yes, in particular it is a (compound) type, not an expression
category. (lvalue and rvalue are *not* types, of course.)
| Quote: | It also has
the well-known problem of describing run-time behaviour
when lvalues are actually identified at translation time.
Para 2: "Some rvalue expressions...refer to objects." This
just confuses the issue rather than drawing a clear
distinction between the two.
|
I don't think it's meant to draw a distinction. Paragraphs 4, 5 and 6
(and parts of Clause 5) provide the rules for distinguishing lvalues
from rvalues. Paragraph 2 is just telling us to what lvalues and
rvalues may refer. In particular, lvalues always denote either
objects or functions, and never anything else. Some rvalues denote
objects; others don't.
The standard is stragely silent at this point about what rvalues which
do not denote objects actually denote. The answer, I think, boils
down to non-object values, which in turn means values that only exist
in a register. Since these values do not occupy a region of storage,
they are not objects (despite having an object type, such as int or
double). The classic example is the return value from a function
which returns a built-in type by value.
| Quote: | Para 5. If lvalues and rvalues are expressions, then the
results of function calls cannot be lvalues or rvalues - only
the function call expressions can be.
|
Yes, the wording here could be improved. Or dropped -- I think 5/6
may convey the necessary information.
| Quote: | Para 6: "An expression which holds a temporary object..." How
can an expression "hold" an object? Surely this should refer
to something like "A cast expression that potentially yields a
temporary object"?
|
Yes, the standard is mostly pretty consistent about saying that
expressions "denote" or "refer to" or "designate" objects, so "holds"
seems a strange thing to say here. Paragraph 6 may actually be
redundant with 5.4/1.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
thp@cs.ucr.edu Guest
|
Posted: Tue Oct 21, 2003 6:45 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
johnchx <johnchx2 (AT) yahoo (DOT) com> wrote:
+ [email]thp (AT) cs (DOT) ucr.edu[/email] wrote
+>
+> 1) Nice work.
+>
+
+ Thanks, and thanks for the comments as well.
+
+> 2) You omitted initialization of const references via rvalues.
+>
+
+ Well, they're in there if you know where to look. They're the
+ "rvalue-to-lvalue 'conversion'" I refered to. (At least that's one
+ way an rvalue-to-lvalue "conversion" can occur.)
But you didn't indicate that it could happen automatically, and AFAIK
it doesn't happen automatically in any other context.
[...]
+> 3) I don't understand what you mean by "the concepts of type an
+> lvalue/rvalue-ness are completely orthogonal ..." By definiton,
+> they are complements of each other, in the sense that every
+> expression is one or the other but never both.
+>
+
+ That should read "the concepts of type AND lvalue/rvalue-ness are
+ completely orthogonal ... " That is, every expression (a) has a type
+ and (b) has an lvalue/rvalue-ness, and (a) is orthogonal to (b), with
+ certain exceptions.
Ah, so.
+ I put it this way to set up the notion that the purpose of reference
+ type is to allow one of these dimensions (type) to affect the other
+ (lvalue/rvalue-ness).
But, in that case, they're not "orthogonal".
[...]
+ What I'm driving at here is the notion that there are two different
+ aspects of a referent -- its location and its value -- which may be
+ denoted by an expression, and lvalues denote the first aspect while
+ rvalues denote the second.
In some cases an rvalue denotes an object, which in general is
somewhat different from that object's value.
+ I've found this to be a useful intuitive guide, in the sense that when
+ I think about expressions this way, I can accurately "guess" what the
+ standard will say, and I can explain things about the standard that
+ would otherwise be mysterious (e.g. when does the function - to -
+ pointer-to-function conversion occur?).
That's the best that any paradigm can accomplish.
+> 5) In the clause:
+>
+> an expression with reference type is interpreted as an
+> lvalue, regardless of the other rules which might apply
+>
+> I'd drop "interpreted as"
+
+ Well...I'm trying to draw attention to the idea that having reference
+ type changes the interpretation of an expression which, if it didn't
+ have reference type, would have been an rvalue.
All lvalues are subject to lvalue-to-rvalue conversions, whether or
not they are lvalues by virtue of having reference type. (But you
knew that, so perhaps you had something else in mind.)
+> and add "denoting the referent
+> of the corresponding reference.
+
+ All expressions denote their referents.
That's certainly the case in say Algol68, but in C++ many people have
serious objection to saying that the expression "3" *refers* to the
integer three.
+ lvalue/rvalue-ness and having
+ or lacking reference type doesn't change this. (I know that the
+ suggested language is more or less a quotation from the standard...I
+ suppose that the import of this phraseing is that the implementation
+ is prohibited from, e.g., copying the referent and treating the
+ expression as denoting the copy. So it probably does need to be in
+ the formal text, but I think it complicates the exposition.)
AFAIK, the exposition in the standard normally mentions specifically
what entity each sort of expression denotes.
+ Another point to mention: I've tried to avoid mixing notion of
+ "reference type" with the terms "a reference" or "the reference."
One can do that by distributing the duty of specifying what object
each sort of expression of reference type denotes one level down to
the clauses the semantics of each operator whose return value has
reference type. But things get more tedious.
+ There are (at least) four distinct language constructs that we
+ commonly refer to as "a reference," and I think that some of the
+ difficulty that we've seen in defining "a reference" comes from trying
+ to trying to find some phraseology that applies equally well to all
+ four constructs.
+
+ I think it is possible to give a single coherent account of the
+ meaning of reference type. I don't think "a reference" has a single,
+ unified definition and meaning.
To fully characterize any language's notion of reference requires more
than one clause. Attempts like "reference are aliases" degenerate
into humpty-dumpty games on the term "alias". Analogies such as
"references behave like implicitly dereferenced pointers" require a
number of exception clauses.
+> It may undergo subsequent
+> lvalue-to-rvalue conversion."
+>
+
+ This just follows from the general rules for building up a full
+ expression from sub-expressions, I think. In other words, it may
+ undergo any valid conversion; there's nothing special about the
+ lvalue-to-rvalue conversion in this context.
Of course. The point is to warn newbie readers not to be astonished
when, via two stage implicit conversion (but don't call it that), an
expression of type T& becomes an rvalue of type T.
+> 6) The fact that all post-initialization occurrences of refernces
+> denote the referent makes it impossible directly to reseat
+> a reference and/or determine its location.
+
+ I think this is a confusing notion that arises from thinking about
+ names declared with reference type as "pointer-like." In fact,
+ there's nothing unusual at all about this. Consider:
+
+ int main() {
+
+ int i = 0;
+ int* pi = &i;
+
+ }
+
+ After its definition, "i" is an lvalue which denotes an object of type
+ int. How would you "reseat" it?
The identifier i is statically bound to the corresponding offset
(location) in certain data regions. That binding is not subject to
dynamic rebinding. Most references are dynamically bound to objects.
In most languages references are subject to dynamic rebinding, but in
C++ such rebinding has consciously been precluded because of the
designer's negative experiences with reference rebinding in Algol68.
That preclusion seems noteworthy, not just to me but to the designer
of the langauge, who has noted it in several places in his writing on
the matter.
+ How would you take its address? Not
+ the address of the denoted object, but of "i" itself?
+ "pi" is the same: after its definition, it is an lvalue denoting an
+ object of type int*. You can change the value stored there, but you
+ can't cause "pi" to denote some other object.
Of course not. The identifier "pi" exists only at compile time and
gets bound at compile time. At run time there is nothing left of "pi"
to bind or rebind. By contrast, most references get dynamically
bound, and some have no idenifiers associated with them. References
and identifiers are quite different notions.
+ What you *can* do is
+ "re-seat" the result of *pi. But again, there's no magic. Consider:
+
+ struct A {
+ int* mp1;
+ int* mp2;
+ void reseat( bool b ) { mb = b; }
+ int& foo() { return mb ? *mp1 : * mp2 ; }
+ bool mb;
+ A(int* p1, int* p2 ):
+ mp1(p1), mp2(p2), mb (true) {}
+ };
+
+ int i, j;
+ A a (&i, &j);
+
+ If I change the value of a.mb, I can change the result of a.foo(), an
+ expression with reference type. Have I "re-seated" a reference? Not
+ in any meaningful sense.
q+
+ What I'm getting at here is that the quality of being "un-reseatable"
+ is in no way peculiar to expressions with reference type.
The fact that we can't at run time change the binding of an
identifier, in no way makes the non-reseatability of references
*expected behavior*. Rather, the non-reseatability of references is a
phenomenon that is peculiar to C++ and the result of a somewhat
arbitrary decision by the language's designer. The more appropriate
analogy would be a const object, which gets bound at run-time but
cannot be rebound.
+> To prohibit doing
+> either of those things via indirection:
+>
+> - Pointers, reference, and arrays to/of references are not
+> permitted.
+
+ I think it's confusing to view these as special prohibitions. They
+ simply follow from the definition of the compound types. Pointers are
+ pointers to void or to objects or functions of a given type.
+ Refereces are references to objects or functions of a given type.
+ Arrays are arrays of objects of a given type.
+
+ Saying that you can't have a pointer to "a reference" is, in my view,
+ a lot like saying that you can't have a pointer to a namespace -- the
+ idea is non-sensical.
It is something that has been prohibited by design. References are an
idea imported into C++ from other language and deliberately crippled.
All I'm saying is that the results of that crippling are noteworthy,
and the designer himself finds them noteworthy. He seems not to
believe that they go without saying.
+> - Struct-or-class objects having reference members have
+> no layout rules.
+>
+
+ Doesn't 9.2/12 apply?
Oops! Apparently I have a misimpression here. The appropriate claim
would be that:
There is no guarantee that a pointer to a struct-or-class object
having a reference points to the object's first member.
The significance of this is that replacing post-initialization
occurrences of referneces by similarly dereferenced pointers can cause
such an object to become POD and subject to more rigid layout rules.
+> 7) By definition, sizeof(T&) denotes sizeof(T).
+
+ Yes, though I think the only reason that there's a special rule on
+ this is that adjusting the type of an expression from T& to T might be
+ considered a step in the evaluation of the expression, and sizeof is
+ guaranteed not to evaluate its operand.
But T& is not an expression and is not subject to evaluation.
+ Thanks again for the thought-provoking comments!
Any time.
Tom Payne
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
David Abrahams Guest
|
Posted: Tue Oct 21, 2003 6:45 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
Gabriel Dos Reis <gdr (AT) integrable-solutions (DOT) net> writes:
| Quote: | dave (AT) boost-consulting (DOT) com (David Abrahams) writes:
| [email]thp (AT) cs (DOT) ucr.edu[/email] writes:
|
| > It might be worthwhile mentioning that some
| > lvalues, e.g., "*(int*)0" don't represent objects
|
| Are you sure?
|
| *(int*)0
|
| is not even a valid expression.
Consider sizeof(*(int*)0).
|
OK, but *(int*)0 isn't evaluated. I should have said it's undefined
behavior to evaluate it. I don't see how it can be defined to be an
lvalue and produce undefined behavior at the same time.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
Gabriel Dos Reis Guest
|
Posted: Tue Oct 21, 2003 7:20 pm Post subject: Re: Defining lvalue, rvalue, pointer and reference |
|
|
[email]dave (AT) boost-consulting (DOT) com[/email] (David Abrahams) writes:
| Quote: | Gabriel Dos Reis <gdr (AT) integrable-solutions (DOT) net> writes:
[email]dave (AT) boost-consulting (DOT) com[/email] (David Abrahams) writes:
| [email]thp (AT) cs (DOT) ucr.edu[/email] writes:
|
| > It might be worthwhile mentioning that some
| > lvalues, e.g., "*(int*)0" don't represent objects
|
| Are you sure?
|
| *(int*)0
|
| is not even a valid expression.
Consider sizeof(*(int*)0).
OK, but *(int*)0 isn't evaluated. I should have said it's undefined
behavior to evaluate it.
|
agreed.
So, *(int*)0 is a valid expression. It invokes an undefined behaviour
only if it is evaluated. Now, consider
double g(int&) { return 0; }
int main()
{
return sizeof (double) != sizeof (g(*(int*)0));
}
this program is well-defined because the operand of sizeof is never
evaluated. It must type-check. But then, that means the expression
*(int*)0 is bound to the (non-)reference parameter int&. That cannot
happen if *(int*)0 is an rvalue.
| Quote: | I don't see how it can be defined to be an
lvalue and produce undefined behavior at the same time.
|
if isn't an lvalue, are you saying it is an rvalue?
--
Gabriel Dos Reis
[email]gdr (AT) integrable-solutions (DOT) net[/email]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|