Revistiing using return value as reference

J

James Kanze

Point taken. But, if an alternate implementation of references ever
comes out, I'd like to know how it works too, because that's just the
curious person I am.

The C++ standard says explicitly that it is unspecified whether
a reference occupies memory or not. All compilers I know do
implement it as a pointer in some cases, and all implement it
without using any memory what so ever in others, by simply
tracking at compile time what is refered to.
 
J

James Kanze

On Dec 28, 8:04 pm, (e-mail address removed) wrote:
That might be true for you and I do agree that lifetimes and
such are far more important things to worry about. But, I
don't see the harm in thinking of them as ptrs.

To a point, I agree. One can say that C++ has two types of
pointers, one of which is called a reference. The problem is
that the second one is called a pointer in all of the existing
literature, so using the term pointer for both would be a major
source of confusion.

The important point is that in C++, pointers and references do
have significant and important differences. More so, almost,
than pointers and ints. (Both ints and pointers are objects,
with an address. For that matter, you can do arithmetic on
pointers as well.) And that the two types of "pointers" that
C++ has, are far from the only two types one can imagine.
 
J

James Kanze

Actually I would think, but I could be wrong, that the idea
comes from TC++PL where it says in §5.5 (freely translated
from Swedish) "A reference is an alternative name for an
object." But then again, BS is an academic.

Now. He wasn't when he wrote TC++PL. From the introduction to
references in the first edition of TC++PL: "A reference acts as
a name for an object". This is copyright 1986---more than 15
years, I think, before Stroustrup moved from Bell Labs to Texas
A&M. Whatever else one may say about it, C++ is firmly grounded
in pragmatic practice, and is not a product of "academica".
 
J

johanatan

That's an interesting example, and a very good reason for not
allowing using an rvalue to initialize a reference.  Still, the
original language (pre-1988, roughly, so we're talking about a
very long time ago) did allow using an rvalue to initialize any
reference, with more or less the same semantics which currently
apply when an rvalue is used to initialize a reference to const.
The rule was changed because it was found, in practice, to be
too great a source of errors.  IMHO, the real problem is that
there are too many implicit conversions, and that the result of
an implicit conversion is an rvalue; the errors are typically
cases where an implicit conversion resulted in an rvalue when
the programmer expected an lvalue.  Things like:

    void
    increment( int& i )
    {
        ++ i ;
    }

    int
    main()
    {
        unsigned x = 0 ;
        increment( x ) ;
        std::cout << x << std::endl ;
        return 0 ;
    }

which, of course, results in 0.

Ahh. That's very interesting. This is the kind of bkg info I was
interested in.

Thanks,
Jonathan
 
J

johanatan

I do not engage in reading minds but only in reading what people write. Your
claim did read like so:

  someone who knows how the inner parts work in practice will
  be better able to make decisions than those who don't

and neither like this:

  someone who knows how the inner parts work and knows the precise rules
  will be better able to make decisions than those who don't.

nor like this:

  someone who knows how the inner parts work and knows the precise rules
  will be better able to make decisions than those who only know the rules.

I figured that both hypothetical persons knew what I referred to in an
earlier post as the 'black-box' view (i.e., and which you are
referring to as the precise rules). That was included at the bottom
of the post that introduced the Stanford quote and the suggestion that
they become a 'Java school' if hiding such details is really that
important.
Besides, if you read what you quote carefully, you will find that I did not
contradict you anyway. I just provided an additional point of view.

Well, your comment didn't include any sort of conjunction (such as
'and') so I suppose that led me to believe that the point was offered
in contrast to mine. I apologize if it wasn't intended that way.
With regard to your clarified position, I find that the situations where
knowledge of the inner workings of references (given a particular compiler
and in addition to knowledge of the rules from the standard) is useful in
decision making are rather limited (in fact, just about nothing comes to
mind). However, that might just be a lack of imagination and experience on
my part. Could you provide some examples for the kind of decision making
you are thinking of?

Well, the first and most obvious one that comes to mind is-- parameter
payloads (pushing and popping as little as possible thereby using as
little stack space as possible, for say, a recursive function though
not necessarily so). I've never had to do much of this in practice
either, but I think that the concepts of 'reference' and 'pointer' in
the abstract are still equivalent (a position supported by the first
sentence of the Wikipedia entry on 'hard links').

However, much of this debate has been about both 'conceptual' and
'technical' understandings of references. Surely, at least now, my
position on the conceptual part is understandable? And, in practice,
since at least some of the time a compiler will compile references to
pointers, the technical part should be understandable too. Whether
that 'white-box', technical info is useful or not is quite another
matter. I wouldn't say that any knowledge is *never* useful though.

Even though I haven't changed any previous held (abstract) positions
drastically through this discussion, I certainly have learned some
things (such as how the compiler can track references and use no
memory slots for holding them) so now my 'white-box' view is even
better than before.

Thanks,
Jonathan
 
J

jkherciueh

johanatan said:
On Dec 30, 2:13 pm, (e-mail address removed) wrote: [snip]
With regard to your clarified position, I find that the situations where
knowledge of the inner workings of references (given a particular
compiler and in addition to knowledge of the rules from the standard) is
useful in decision making are rather limited (in fact, just about nothing
comes to mind). However, that might just be a lack of imagination and
experience on my part. Could you provide some examples for the kind of
decision making you are thinking of?

Well, the first and most obvious one that comes to mind is-- parameter
payloads (pushing and popping as little as possible thereby using as
little stack space as possible, for say, a recursive function though
not necessarily so).

I considered this before I posted and, upon reflection, did not count it as
a case where knowing the inner workings allows to make better decisions.
The reason is that there is a way to make a non-worse and possibly better
decision that does not require the knowledge in question.

The decision is essentially between

possibly_void_return_type f ( T );

and

possibly_void_return_type f ( T const & );

The two signatures are indistinguishable from a client point of view. Thus,
choosing between them is a matter of performance ad resource usage. In my
experience, this decision should be strictly based upon _measurement_. In
particular, even knowing that T const & is treated by the compiler as if a
pointer was passed by value, you still have to measure performance and
resource consumption in order to choose rationally. At least, your decision
will not be worse and it might end up better than a decision based upon a
mental model of what the compiler might do.

I've never had to do much of this in practice
either, but I think that the concepts of 'reference' and 'pointer' in
the abstract are still equivalent (a position supported by the first
sentence of the Wikipedia entry on 'hard links').

I fail to grasp how a wikipedia entry on hard links pertains to the concepts
of references and pointers (note that in the context of C++, these are
highly technical terms strictly defined by the standard with a meaning that
only loosely relates to their ordinary language usage).

However, much of this debate has been about both 'conceptual' and
'technical' understandings of references. Surely, at least now, my
position on the conceptual part is understandable?

Yes. At least, I think, I understand your position. Based upon my
understanding, I still don't consider it helpful.

My attitude toward references (i.e., that understanding what they are is
hopeless and not helpful) is strongly influenced by what the discussions on
this group have taught me. For instance, I learned about the rule that a
non-const reference cannot be bound to an rvalue through examples like
this, which I presented else-thread:

int main ( void ) {
std::eek:fstream( "test.001" ) << std::dec << "hello world!" << '\n';
std::eek:fstream( "test.002" ) << "hello world!" << '\n';
}

Then, I learned about a trick to circumvent this restriction via:

/*
| lvalue_cast( non-const expression ) creates an lvalue out
| of anything, including a temporary. Just make sure, the
| expression is not constant.
*/

template < typename T >
T & lvalue_cast ( const T & ref ) {
return( const_cast< T & >( ref ) );
}

and used it for instance to default-initialize non-const reference
parameters (there are very rare cases, where that makes sense):

T some_fct ( S param, T & goal = lvalue_cast<T>( T() ) ) {
// some recursive search involving some_fct( other_par, goal );
return ( goal );
}

Finally, I learned in this thread

http://groups.google.com/group/comp.lang.c++/browse_frm/thread/e8c8fb5450c8182d

that the careful reading of the standard shows the code above to have
undefined behavior.


On the other hand, when T is a class-type and has an assignment operator

T & operator= ( T const & );

you could say

T some_fct ( S param, T & goal = ( T() = T() ) ) {
// some recursive search involving some_fct( other_par, goal );
return ( goal );
}

to do the same, and it would have well-define behavior.


It is experiences like these that taught me that references are much much
more wicked than any of the pictures predicted that I previously had of
them.

And, in practice,
since at least some of the time a compiler will compile references to
pointers, the technical part should be understandable too. Whether
that 'white-box', technical info is useful or not is quite another
matter. I wouldn't say that any knowledge is *never* useful though.

Right: since I was not able to rule out the possibility that the particular
piece of knowledge you are advertising could be useful under some
circumstances, I decided to ask for examples.

Even though I haven't changed any previous held (abstract) positions
drastically through this discussion, I certainly have learned some
things (such as how the compiler can track references and use no
memory slots for holding them) so now my 'white-box' view is even
better than before.

Then, this discussion has already paid off. That's good.


Best

Kai-Uwe Bux
 
J

johanatan

Not really.  A C++ reference is a more or less an alias for an
object.  It can't be null, and it can't be reseated.

Ok, now I understand the term 'alias' a little more after seeing that
it describes those limitations of references a little better. But, as
with most CS concepts, alias can mean alot of different things in
different environments. Take bash shell scripts for instance, it's
just a way of 'rewriting' a command. Or, a string that represents
another string. Or, a symbol that refers to another symbol. Or, a
link. Or, a pointer. All these concepts at the abstract level seem
pretty equivalent.

And, if you want to start getting into the differences at the
technical level in any given environment, then you have to learn the
differences and the names are there simply to provide a common
vocabulary (but conceptually, do we always think in a language? What
is the essence of these concepts if you set aside the differences in
names?)

The name of something is not what matters, but its essence. Names are
only there to make communication easier (and with the 'attached'
conceptions to most names especially when going from one computer lang/
platform/lib to another they serve to confuse a great deal).
Comparisons with Java and C# don't make much sense, either,
because the object model is different: in C++, a pointer is an
object, just like an int or anything else, and you can take the
actual address of anything---in Java (and C#?), only class types
can be objects, you can only have a reference (or pointer) to a
class type, and all objects of class types must be dynamically
allocated.  Globally considered, references in Java are as exact
an equivalent to C++ pointers as you can have, given the object
model of Java.

Not really, references behave syntactically the same in C++ and Java
(except for the use of the &). Everything is by default a reference
in Java, and you can't get at the pointers. Maybe C# is a more
relevant example because with it, you can have an 'unsafe' block and
still get to the actual underlying pointers. But, in my mind, a
reference in all 3 langs is essentially the same.
Not in any Java implementation I've seen or heard of.

Actually, they are smart. But, I was mistaken about the ref-counted
part. The garbage collector traverses all 'live' ptrs and deletes
those that it doesn't reach.
Like C++ pointers.


Java doesn't have rvalues, at least, not in the sense C++ does.

What about this piece of code:

foo f1;
foo f2;

someFunc(f1 + f2);

f1 + f2 is an r-value and it can be passed directly to someFunc
assuming that it accepts foo or object types.
In C++, a pointer can be set multiple times, as in Java.  And
you need to jump through hoops to get it to point to an rvalue,
since you cannot directly take the address of an rvalue (and of
course, an object allocated by new is not an rvalue).

So far, you've shown that references in Java are the equivalent
of pointers in C++, not the equivalent of references.

No, references in Java are very different from pointers. You can't
actually modify a reference except by setting it equal to another
object or null. The underlying ptr is inaccessible. You also cannot
use multiple levels of indirection with Java references. There is no
& (address) operator in Java as you never need to worry about
addresses. The reference abstracts that away. Much the same way a C+
+ reference does.
No.  I think that the original motivation for references in C++
is linked with operator overloading; they allow writing an
operator+ which doesn't require a deep copy of both of its
arguments, but can still be called with the usual syntax.  The
designers of Java based there references on C++ (and C)
pointers, simply eliminating features which they felt were too
dangerous (like pointer arithmetic) or which didn't fit into the
object model.  (For that matter, pointer arithmetic really
wouldn't fit into the Java object model either.)  The choice of
the name was purely based on the supposed reputation that
pointers were dangerous, and that in a "safe" language, you
can't have pointers.  (Marketing, in sum.)

Well, I think we agree on most of that except that refs allow you to
prevent deep copy on many other overloaded operators too (copy
constructor, etc) and whatever custom code you want to write to pass
something 'by reference'. So I find it hard to believe that operator+
was the sole or even primary motivation. Also, It is very hard to
convince me that the word reference was chosen completely
independantly of the notion of 'passing something by reference' in C
(which was done with ptrs).

The other difference we have is that I think that Java designers chose
the word 'reference' exactly because of the semantics of reference in C
++, but simply cleaned it up a bit and added features to it. It
certainly feels a lot more like a C++ reference than a C++ pointer
simply because you don't have to dereference or use the -> operator
(and you can use the . operator).

The reference in C++ was supposed to be an improvement over pointers.
But, since it kept pointers for backwards compatibility with C, it
couldn't universally use refs. Java didn't have that problem so could
add what was needed to reference (namely setting to NULL and allowing
resets) and therby improve upon the C++ notion of reference.

When you have two ways of doing something, you have to figure out how
to limit each way (or else they both simply become exact clones).
But, if there's only one way, you can implement the minimal featureset
(and produce a cleaner design IMHO). And, the fact that the designers
of Java named it 'reference' instead of 'pointer' seems to be
indicative which of the two ideas they thought it was more similar to.

--Jonathan
 
J

johanatan

I considered this before I posted and, upon reflection, did not count it as
a case where knowing the inner workings allows to make better decisions.
The reason is that there is a way to make a non-worse and possibly better
decision that does not require the knowledge in question.

The decision is essentially between

  possibly_void_return_type f ( T );

and

  possibly_void_return_type f ( T const & );

The two signatures are indistinguishable from a client point of view. Thus,
choosing between them is a matter of performance ad resource usage. In my
experience, this decision should be strictly based upon _measurement_. In
particular, even knowing that T const & is treated by the compiler as if a
pointer was passed by value, you still have to measure performance and
resource consumption in order to choose rationally. At least, your decision
will not be worse and it might end up better than a decision based upon a
mental model of what the compiler might do.

That's true. But there is a set of general practices (some even
advertised in Scott Meyer's Exceptional C++ series) which produce more
efficient codes than others.

In the past, I've always minimized CPU cycles. But, now that CPUs are
so quick and we have multi-core chips, minimizing memory usage can be
even more important (just because of page faults and not necessarily
the lack of memory).

So, yea, you can't really tell which is more performant until you
measure. But something about passing a heavy object by value on the
stack doesn't sound good to me (especially if the function is
recursive) under any CPU or mem constraints.
I fail to grasp how a wikipedia entry on hard links pertains to the concepts
of references and pointers (note that in the context of C++, these are
highly technical terms strictly defined by the standard with a meaning that
only loosely relates to their ordinary language usage).

That only applies to the 'conceptual' or abstract part of this
debate. Just reading the first sentence shows that, in concept,
reference and pointer are synonomous. It's only when you start to
look at applications of the notions in practice that you need to start
worrying about technical details. At some point, a couple of posters
told me that it was wrong to even conceptualize references as
pointers, so I figured that an example of the terms being using
interchangeably would be pretty solid evidence that they are in fact
identical concepts (at least in some context).
Yes. At least, I think, I understand your position. Based upon my
understanding, I still don't consider it helpful.

It's simply a synonym. It's not really provided as a mental model (as
the term 'alias' seems to be). In technical communications, sure, it
helps to standardize on some vocabulary, but, in my mind, the two
concepts are very similar.
My attitude toward references (i.e., that understanding what they are is
hopeless and not helpful) is strongly influenced by what the discussions on
this group have taught me. For instance, I learned about the rule that a
non-const reference cannot be bound to an rvalue through examples like
this, which I presented else-thread:

  int main ( void ) {
    std::eek:fstream( "test.001" ) << std::dec << "hello world!" << '\n';
    std::eek:fstream( "test.002" ) << "hello world!" << '\n';
  }

Then, I learned about a trick to circumvent this restriction via:

  /*
  | lvalue_cast( non-const expression ) creates an lvalue out
  | of anything, including a temporary. Just make sure, the
  | expression is not constant.
  */

  template < typename T >
  T & lvalue_cast ( const T & ref ) {
    return( const_cast< T & >( ref ) );
  }

and used it for instance to default-initialize non-const reference
parameters (there are very rare cases, where that makes sense):

  T some_fct ( S param, T & goal = lvalue_cast<T>( T() ) ) {
    // some recursive search involving some_fct( other_par, goal );
    return ( goal );
  }

Finally, I learned in this thread

http://groups.google.com/group/comp.lang.c++/browse_frm/thread/e8c8fb...

that the careful reading of the standard shows the code above to have
undefined behavior.

On the other hand, when T is a class-type and has an assignment operator

  T & operator= ( T const & );

you could say

  T some_fct ( S param, T & goal = ( T() = T() ) ) {
    // some recursive search involving some_fct( other_par, goal );
    return ( goal );
  }

to do the same, and it would have well-define behavior.

It is experiences like these that taught me that references are much much
more wicked than any of the pictures predicted that I previously had of
them.

Thanks for these ideas. I will have to digest them a bit, but they
look very interesting.

Yea, but these aren't problems with the abstract notions of
'references' or 'pointers' or 'aliases'. This 'wickedness' has
emerged from a set of imposed constraints each of which follow simple
rules but together form a very complex environment.
Right: since I was not able to rule out the possibility that the particular
piece of knowledge you are advertising could be useful under some
circumstances, I decided to ask for examples.

I'm sure there must be more examples and I will post them when I think
of them.
Then, this discussion has already paid off. That's good.

Yea, definitely, this discussion has proven very worthwhile (and will
be even more so after I digest those example codes you posted).

Best regards,

--Jonathan
 
J

James Kanze

Ok, now I understand the term 'alias' a little more after
seeing that it describes those limitations of references a
little better. But, as with most CS concepts, alias can mean
alot of different things in different environments.

Which is the real problem here. The concept of pointer can mean
a lot of different things in different environments. As can the
concept of reference. And amongst the different meanings,
there's a lot of overlap between pointers and references. From
a purely CS point of view, C++ pointers, C++ references, and
Java pointers could all be called "pointers"; from a purely CS
point of view, I have difficult accepting that something which
can be reseated and which can be null can be called a reference,
but this may be largely because I first encountered the term in
a CS context in C++ (although if I understand correctly,
Stroustrup picked up much of the concept, and the name, from
Algol68, where they could be reseated).
Take bash shell scripts for instance, it's just a way of
'rewriting' a command. Or, a string that represents another
string. Or, a symbol that refers to another symbol. Or, a
link. Or, a pointer. All these concepts at the abstract
level seem pretty equivalent.

Yes. I think that roughly speaking, an "alias" is a different
way of saying the same thing. It may involve two different
names for the same thing (most people, I think, would consider a
link in the file system to be an "alias"), or a short name for
an otherwise complex "expression", or perhaps even vice versa: a
calculated expression replacing a (constant?) fixed name.

"Alias" is an almost perfect characterization of references when
both the reference and its initializer are named objects, e.g.:

int i ;
int& r = i ;

In such cases, whether you use r or i is totally indifferent;
the results will be exactly the same.

This works very well with return values as well; if foo()
returns a reference to myValue, then the expression foo() works
as an "alias" to "myValue".

It becomes harder to fathom when you're dealing with const
references initialized with an rvalue. Mainly because, without
the reference, there wouldn't necessarily be any object, and the
C++ standard is very clear, a reference must designate an
individual object.

And it tends to become really shaky when you start dealing with
casts to reference types. (But at that point, so does viewing
references as pointers, since there will usually be no pointer
involved.)
And, if you want to start getting into the differences at the
technical level in any given environment, then you have to
learn the differences and the names are there simply to
provide a common vocabulary (but conceptually, do we always
think in a language? What is the essence of these concepts if
you set aside the differences in names?)
The name of something is not what matters, but its essence.
Names are only there to make communication easier (and with
the 'attached' conceptions to most names especially when going
from one computer lang/ platform/lib to another they serve to
confuse a great deal).

Agreed. In the end, C++ has two concepts, one which it inherits
from C, and calls "pointer", and the other which was created
expressedly for C++ (principally, originally, to support
operator overloading), and is called "reference". For
historical reasons, "pointers", in C++, share a number of
characteristics with other arithmetic types, such as ints.
Also, because of why they were introduced, and because C++
already had pointers, references in C++ are very, very
restricted. Most significantly, references in C++ are not
"objects", that is, they aren't integrated in the C++ object
model. At least formally, because there are contexts where they
do behave sort of like objects: they have a lifetime, for
example.

At the risk of missing something: at runtime, C++ has 3 types of
entities: objects, references and functions. Objects and
references have lifetime, functions don't. Objects and
functions have addresses, references don't. Only objects have
size. Etc., etc. The C++ standard is worded in these terms;
other wordings are possible and still give the same actual
behavior.

The object model of Java is more complex, in that you have two
major categories of data: basic types and references/objects: a
variable in Java can never be an object (as "object" is defined
in Java), and you can't "create" a reference arbitrarily---only
declare one. (References in Java behave a lot like the other
basic types, in fact.)
Not really, references behave syntactically the same in C++
and Java (except for the use of the &). Everything is by
default a reference in Java, and you can't get at the
pointers. Maybe C# is a more relevant example because with
it, you can have an 'unsafe' block and still get to the actual
underlying pointers. But, in my mind, a reference in all 3
langs is essentially the same.

The syntax for using references in Java is similar to the syntax
for using references in C++. The semantics of references in
Java is much closer to the semantics of pointers in C++,
however. If you think of references in C++ as pointers with
some syntactic suger, then Java references a similar to C++
references. If you think of references and pointers in C++ as
being two different things, which his how they are defined in
the standard, then references in Java are closer to pointers in
C++ than they are to C++ references: things like no null
references or no reseating of references are fundamental to the
C++ definition of what a reference is.
Actually, they are smart. But, I was mistaken about the
ref-counted part. The garbage collector traverses all 'live'
ptrs and deletes those that it doesn't reach.

The pointers themselves aren't necessarily smart; I'm pretty
sure that a Java implementation can use exactly the same garbage
collector, in exactly the same circumstances, as a C++
implementation. Of course, it's a question of terminology, but
I'd say that with garbage collection (required by the Java
specification, optional in C++), you don't need smart pointers
for most memory management because the memory manager itself is
intelligent. The intelligence is concentrated in one small
component which the programmer doesn't need to deal with, rather
than being spread throughout the program where the programmer is
constantly tripping over it.
What about this piece of code:
foo f1;
foo f2;
someFunc(f1 + f2);
f1 + f2 is an r-value and it can be passed directly to
someFunc assuming that it accepts foo or object types.

In Java, that isn't legal code, since there's no user defined
operator overloading, and the operator+ is only defined for
basic types and java.lang.String.

In C++, a first approximation of the difference between lvalues
and rvalues is that lvalues have an address, and rvalues don't.
(In fact, rvalues of class types do have an address.) In Java,
this distinction doesn't really exist; basic types and
references don't have an address, and objects do, regardless of
how the entity was formed.

Of course, there are limitations with regards to the expressions
which can appear on the left side of an assignment operator (or
be an operand to ++, etc.). One could call this an
lvalue/rvalue distinction. But it would be somewhat different
than the same distinction in C++. And more importantly, the
Java specification doesn't use this language; it refers to
"variables" and "values" (which in many ways do correspond to
lvalues and rvalues).

Again, each language has its own vocabulary, for many historical
reasons. And because the languages are different, there isn't
necessarily a one to one relationship between the different
vocabulary.
No, references in Java are very different from pointers. You
can't actually modify a reference except by setting it equal
to another object or null.

And? That's the case for pointers in most languages, as well.
Are you saying that the difference is that you cannot obtain the
address of anything, except by allocating it dynamically?
That's an artifact of the Java object model (and affects other
languages, such as Pascal or Modula-2, which only have
"pointers", and not "references").
The underlying ptr is inaccessible.

So. It's true that in C++, you can cast the address of anything
to an unsigned char*, and hex dump it, to see the low level,
shallow representation. But that doesn't mean much; I've used
machines where the mapping between this representation and the
actual address in memory was anything but transparent.
You also cannot use multiple levels of indirection with Java
references.

Again, that's because of the Java object model, which
distinguishes between objects and variables.
There is no & (address) operator in Java as you never need to
worry about addresses. The reference abstracts that away.
Much the same way a C+ + reference does.

I'm not sure that the C++ reference does abstract that away. If
"r" has type int&, I can still apply the address of operator to
it.
Well, I think we agree on most of that except that refs allow
you to prevent deep copy on many other overloaded operators
too (copy constructor, etc) and whatever custom code you want
to write to pass something 'by reference'. So I find it hard
to believe that operator+ was the sole or even primary
motivation.

The example Stroustrup uses to introduce references in the
original TC++PL was operator<<. It's true that without
references, you can't define a copy constructor; I wonder how
that was handled before references were introduced into the
language. (I know that the reason "this" is not a reference is
because when it was introduced, there were no references, so
references were not present in the very oldest forms of the
language. That was before I was using it, however.)
Also, It is very hard to convince me that the word reference
was chosen completely independantly of the notion of 'passing
something by reference' in C (which was done with ptrs).

According to Stroustrup, the word was suggested to him by Doug
Ilroy, and the context was its use in Algol68. Don't forget
that Stroustrup knew a lot of other languages, beside C, and
didn't hesitate to incorporate a good idea, regardless of where
it came from.
The other difference we have is that I think that Java
designers chose the word 'reference' exactly because of the
semantics of reference in C ++, but simply cleaned it up a bit
and added features to it. It certainly feels a lot more like
a C++ reference than a C++ pointer simply because you don't
have to dereference or use the -> operator (and you can use
the . operator).

I don't think so. About the time Java was introduced, there was
a great deal of criticism concerning pointers, largely based on
the fact that in C (and C++), they could (and did) end up
pointing to anything, even to things that didn't exist in the
program. Most of the real problems were in fact due to the fact
that arrays, in C (and in C++) are broken, and that array
operations end up being pointer operations, with no
possibilities of bounds checking, etc. This is not something
fundamental to pointers, and in fact, is not a characteristic of
pointers in any other languages I know. But it had given
pointers a bad name. In fact, the Java concept of reference is
very, very similar to Modula-3 pointers. I'm fairly sure that
the creators of Java were familiar with Modula-3---Java adopts
several concepts directly from Modula-3, and the only reason I
can imagine that they didn't call a pointer a pointer is because
of the bad press pointers were getting at that time
(undeservedly, since the problem was really the fact that arrays
weren't first class objects).
The reference in C++ was supposed to be an improvement over
pointers. But, since it kept pointers for backwards
compatibility with C, it couldn't universally use refs.

That is not historically correct. C++ existed for a time
without references, and according to Stoustrup, references were
introduced in response to problems defining operator
overloading. There was never any time that Stroustrup (or
anyone else I've talked to) considered that it would be better
if the language didn't have pointers.
Java didn't have that problem so could add what was needed to
reference (namely setting to NULL and allowing resets) and
therby improve upon the C++ notion of reference.

One thing Java did do more or less right: arrays are real, first
class types, which behave like every other type (or at least,
like every other class type). Because of this, and because Java
doesn't support low level programming (you can't write a garbage
collector in Java), it didn't need pointer arithmetic. Once
they'd abandonned the way C handled arrays, they were free to
adopt the pointer concept from any one of a number of existing
languages---as I said, it is very similar to that of Modula-3
(from memory---it's been a long time since I last looked at
Modula-3).
When you have two ways of doing something, you have to figure
out how to limit each way (or else they both simply become
exact clones). But, if there's only one way, you can
implement the minimal featureset (and produce a cleaner design
IMHO). And, the fact that the designers of Java named it
'reference' instead of 'pointer' seems to be indicative which
of the two ideas they thought it was more similar to.

What makes you so sure that they were looking at C++, and only
C++? If you pick up any book on algorithms from the time (e.g.
Worth's "Algorithms + Data Structures = Programs"), you'll find
pointers (called pointers) used to implement the dynamic data
structures (like lists and trees). Java's authors obviously
felt the need to support this. They also almost certainly felt
the need to avoid pointer arithmetic, and arrays decaying into
pointers, which are characteristics of the C model for arrays.
But certainly nothing prevented them from adopting pointers from
some other language, and the only possible reason I can conceive
of for not calling them pointers is the bad press that (C)
pointers were getting at that time (or even earlier?: Ada calls
them "access types"---with the note that "Access values are
called ``pointers'' or ``references'' in some other
languages.").
 
E

Erik Wikström

What about this piece of code:

foo f1;
foo f2;

someFunc(f1 + f2);

f1 + f2 is an r-value and it can be passed directly to someFunc
assuming that it accepts foo or object types.

According to the Java Language Specification 3rd ed. §15.1:

When an expression in a program is evaluated (executed), the result
denotes one of three things:
• A variable (§4.12) (in C, this would be called an lvalue)
• A value (§4.2, §4.3)
• Nothing (the expression is said to be void)

A value is of course something of value-type (int, double etc.) and
while they are not like lvalues they are not rvalues either.
 
J

James Kanze

On 2007-12-31 07:00, johanatan wrote:
According to the Java Language Specification 3rd ed. §15.1:
When an expression in a program is evaluated (executed), the result
denotes one of three things:
? A variable (§4.12) (in C, this would be called an lvalue)
? A value (§4.2, §4.3)
? Nothing (the expression is said to be void)

And that parenthese is the only use of either lvalue or rvalue
that I know of in the entire specification. It's interesting,
because it means that an expression like a is a "variable";
we wouldn't consider it a variable in C/C++. Different
languages, different philosophies, different basic models, and a
different vocabulary. (But both do use { and }... two
characters which aren't present on my local keyboards.)
A value is of course something of value-type (int, double
etc.) and while they are not like lvalues they are not rvalues
either.

Note that in Java, you cannot take the address of either a value
or a "variable". In a very real sense, neither has an address;
only objects have addresses.

As I've already said, the object model is completely different,
so trying to push analogies isn't going to work.
 
J

johanatan

Which is the real problem here.  The concept of pointer can mean
a lot of different things in different environments.  As can the
concept of reference.  And amongst the different meanings,
there's a lot of overlap between pointers and references.  From
a purely CS point of view, C++ pointers, C++ references, and
Java pointers could all be called "pointers"; from a purely CS
point of view, I have difficult accepting that something which
can be reseated and which can be null can be called a reference,
but this may be largely because I first encountered the term in
a CS context in C++ (although if I understand correctly,
Stroustrup picked up much of the concept, and the name, from
Algol68, where they could be reseated).


Yes.  I think that roughly speaking, an "alias" is a different
way of saying the same thing.  It may involve two different
names for the same thing (most people, I think, would consider a
link in the file system to be an "alias"), or a short name for
an otherwise complex "expression", or perhaps even vice versa: a
calculated expression replacing a (constant?) fixed name.

"Alias" is an almost perfect characterization of references when
both the reference and its initializer are named objects, e.g.:

    int  i ;
    int& r = i ;

In such cases, whether you use r or i is totally indifferent;
the results will be exactly the same.

This works very well with return values as well; if foo()
returns a reference to myValue, then the expression foo() works
as an "alias" to "myValue".

It becomes harder to fathom when you're dealing with const
references initialized with an rvalue.  Mainly because, without
the reference, there wouldn't necessarily be any object, and the
C++ standard is very clear, a reference must designate an
individual object.

And it tends to become really shaky when you start dealing with
casts to reference types.  (But at that point, so does viewing
references as pointers, since there will usually be no pointer
involved.)


Agreed.  In the end, C++ has two concepts, one which it inherits
from C, and calls "pointer", and the other which was created
expressedly for C++ (principally, originally, to support
operator overloading), and is called "reference".  For
historical reasons, "pointers", in C++, share a number of
characteristics with other arithmetic types, such as ints.
Also, because of why they were introduced, and because C++
already had pointers, references in C++ are very, very
restricted.  Most significantly, references in C++ are not
"objects", that is, they aren't integrated in the C++ object
model.  At least formally, because there are contexts where they
do behave sort of like objects: they have a lifetime, for
example.

At the risk of missing something: at runtime, C++ has 3 types of
entities: objects, references and functions.  Objects and
references have lifetime, functions don't.  Objects and
functions have addresses, references don't.  Only objects have
size.  Etc., etc.  The C++ standard is worded in these terms;
other wordings are possible and still give the same actual
behavior.

The object model of Java is more complex, in that you have two
major categories of data: basic types and references/objects: a
variable in Java can never be an object (as "object" is defined
in Java), and you can't "create" a reference arbitrarily---only
declare one.  (References in Java behave a lot like the other
basic types, in fact.)




The syntax for using references in Java is similar to the syntax
for using references in C++.  The semantics of references in
Java is much closer to the semantics of pointers in C++,
however.  If you think of references in C++ as pointers with
some syntactic suger, then Java references a similar to C++
references.  If you think of references and pointers in C++ as
being two different things, which his how they are defined in
the standard, then references in Java are closer to pointers in
C++ than they are to C++ references: things like no null
references or no reseating of references are fundamental to the
C++ definition of what a reference is.


The pointers themselves aren't necessarily smart; I'm pretty
sure that a Java implementation can use exactly the same garbage
collector, in exactly the same circumstances, as a C++
implementation.  Of course, it's a question of terminology, but
I'd say that with garbage collection (required by the Java
specification, optional in C++), you don't need smart pointers
for most memory management because the memory manager itself is
intelligent.  The intelligence is concentrated in one small
component which the programmer doesn't need to deal with, rather
than being spread throughout the program where the programmer is
constantly tripping over it.


In Java, that isn't legal code, since there's no user defined
operator overloading, and the operator+ is only defined for
basic types and java.lang.String.

In C++, a first approximation of the difference between lvalues
and rvalues is that lvalues have an address, and rvalues don't.
(In fact, rvalues of class types do have an address.)  In Java,
this distinction doesn't really exist; basic types and
references don't have an address, and objects do, regardless of
how the entity was formed.

Of course, there are limitations with regards to the expressions
which can appear on the left side of an assignment operator (or
be an operand to ++, etc.).  One could call this an
lvalue/rvalue distinction.  But it would be somewhat different
than the same distinction in C++.  And more importantly, the
Java specification doesn't use this language; it refers to
"variables" and "values" (which in many ways do correspond to
lvalues and rvalues).

Again, each language has its own vocabulary, for many historical
reasons.  And because the languages are different, there isn't
necessarily a one to one relationship between the different
vocabulary.


And?  That's the case for pointers in most languages, as well.
Are you saying that the difference is that you cannot obtain the
address of anything, except by allocating it dynamically?
That's an...

read more »

I won't bother wasting time by responding to each comment in line, but
*wow* that's some very insightful info. The 'historical'
understanding I had was certainly somewhat fabricated and falsely
extrapolated, but I think we all agree that the abstract notions of
ptrs, references, and aliases are essentially the same and could
probably offer examples of lots of different languages where this or
that aspect is this or that way. That doesn't change the fact that I
can still *think* of them in the abstract as essentially equivalent
concepts. :)

--Jonathan
 
J

johanatan

According to the Java Language Specification 3rd ed. §15.1:

When an expression in a program is evaluated (executed), the result
denotes one of three things:
* A variable (§4.12) (in C, this would be called an lvalue)
* A value (§4.2, §4.3)
* Nothing (the expression is said to be void)

A value is of course something of value-type (int, double etc.) and
while they are not like lvalues they are not rvalues either.

So, does that mean that an expression cannot evaluate to a reference
to an object? Even if operator overloading is prohibited, there are
other expressions that could result in a 'reference' to an object as
the result.

--Jonathan
 
K

Kira Yamato

[...]
The 'historical' understanding I had was certainly somewhat fabricated
and falsely
extrapolated, but I think we all agree that the abstract notions of
ptrs, references, and aliases are essentially the same and could
probably offer examples of lots of different languages where this or
that aspect is this or that way. That doesn't change the fact that I
can still *think* of them in the abstract as essentially equivalent
concepts. :)

Comment like this is prime example of what has become known as "truthiness."

http://en.wikipedia.org/wiki/Truthiness
 
J

James Kanze

So, does that mean that an expression cannot evaluate to a
reference to an object?

Just the opposite. An expression in Java never evaluates to an
object type, only to a variable or a value. Variables and
values can have either a basic type or a reference type; they
are never objects.

Again: the object model in Java is radically different than that
in C++, so any analogies are likely to be misleading.
 
J

johanatan

[...]
The 'historical' understanding I had was certainly somewhat fabricated
and falsely
extrapolated, but I think we all agree that the abstract notions of
ptrs, references, and aliases are essentially the same and could
probably offer examples of lots of different languages where this or
that aspect is this or that way.  That doesn't change the fact that I
can still *think* of them in the abstract as essentially equivalent
concepts. :)

Comment like this is prime example of what has become known as "truthiness.."

http://en.wikipedia.org/wiki/Truthiness

Well, if you want to get that personal, I guess I'll continue
defending myself (though I do not really need to). I highly suspect
that everyone does just this. No one has the time to exhaustively
research each and every detail of their view of the world (let alone
the histories of those views). So, we extrapolate and fill in the gaps
with somewhat speculative information. Are you saying you don't do
the same?

And, furthermore, my views actually were not all that historically
incorrect. If you want, we can get back to the original discussion.
I still do not accept that references are merely 'aliases'. If
anything, the word alias is just as flawed as pointer. A reference is
a reference. I think that's the point that people are drive home.
You have to look at the C++ standard and look at how it defines
'reference.'

Take these two examples:

int x;
int& y;

Yes, in that case, everyone agrees. That's what feels to me like an
'alias'.

But, what about this (somewhat more complex example):

void f(int& x)
{
x = 9;
}

void g(int& y)
{
y = 7;
}

int main()
{
int x = 3;
f(g(x));
}

and really it would help to make the call stack 10 or more levels
deep. There's no way you're going to implement that without using
'pointers'. If anyone can think of such a way, please do share.

Given the close ties between C and C++, it is not unreasonable at all
to think of references as pointers. You can try to indoctrinate the
new students with this notion of 'alias', but it's going to be hard to
erase the memory of pointers from C or assembly programmers.

That's all.
 
J

johanatan

Just the opposite.  An expression in Java never evaluates to an
object type, only to a variable or a value.  Variables and
values can have either a basic type or a reference type; they
are never objects.

Well, given that the 'variables' in Java (which are not values, or
intrinsics) are 'references' (usually to objects), I'd say I'm a
little confused by your remark. Would you mind to please clarify?
 
R

red floyd

johanatan said:
But, what about this (somewhat more complex example):

void f(int& x)
{
x = 9;
}

void g(int& y)
{
y = 7;
}

int main()
{
int x = 3;
f(g(x));
}

No problem, this example wouldn't compile. g returns void (or, more
properly, doesn't return a value), so you can't pass g(x) to f().
 
J

johanatan

No problem, this example wouldn't compile.  g returns void (or, more
properly, doesn't return a value), so you can't pass g(x) to f().

Ooops. Sorry. Meant this:

int main()
{
int x = 9;
f(x);
g(x);
}
 
J

johanatan

On 2007-12-31 21:36:09 -0500, johanatan <[email protected]> said:
[...]
The 'historical' understanding I had was certainly somewhat fabricated
and falsely
extrapolated, but I think we all agree that the abstract notions of
ptrs, references, and aliases are essentially the same and could
probably offer examples of lots of different languages where this or
that aspect is this or that way.  That doesn't change the fact that I
can still *think* of them in the abstract as essentially equivalent
concepts. :)
Comment like this is prime example of what has become known as "truthiness."

--

Well, if you want to get that personal, I guess I'll continue
defending myself (though I do not really need to).  I highly suspect
that everyone does just this.  No one has the time to exhaustively
research each and every detail of their view of the world (let alone
the histories of those views). So, we extrapolate and fill in the gaps
with somewhat speculative information.  Are you saying you don't do
the same?

And, furthermore, my views actually were not all that historically
incorrect.  If you want, we can get back to the original discussion.
I still do not accept that references are merely 'aliases'.  If
anything, the word alias is just as flawed as pointer.  A reference is
a reference.  I think that's the point that people are drive home.
You have to look at the C++ standard and look at how it defines
'reference.'

Take these two examples:

int x;
int& y;

Yes, in that case, everyone agrees.  That's what feels to me like an
'alias'.

But, what about this (somewhat more complex example):

void f(int& x)
{
  x = 9;

}

void g(int& y)
{
  y = 7;

}

int main()
{
  int x = 3;
  f(g(x));

}

and really it would help to make the call stack 10 or more levels
deep.  There's no way you're going to implement that without using
'pointers'.  If anyone can think of such a way, please do share.

Given the close ties between C and C++, it is not unreasonable at all
to think of references as pointers.  You can try to indoctrinate the
new students with this notion of 'alias', but it's going to be hard to
erase the memory of pointers from C or assembly programmers.

That's all.

Please don't bother telling me how an compiler might optimize the int
main() i just posted. Even it is not complex enough to make the
point. You must assume that g(x) and f(x) are called from many
different places in your code with many different variable instances.
And, it'd help if you imagine a call stack at least 10 levels deep
with a single reference passed through to all of them (all of which
are called from many different places with many different variable
instances). In that case, please tell me how that can be 'optimized'
in order to keep the 'alias' mental imagery alive. I would propose
that even though thinking of the reference in that case as a 'pointer'
is technically incorrect according to the C++ standard, it is still
the better mental image of the two (and is in fact, pretty much
exactly what happens in the assembly code).

I think the only way to really say what influenced the C++ reference
would be to ask the designer himself. Hypothesizing that lang X or
lang Y which had a concept of 'pointer' or 'reference' as the main
motivation for the C++ reference is overlooking the 800 lb. gorilla
(who happens to have almost the same name except for those two plus
signs) and is by anyone's account a *huge* influence. How can a
servant be greater than his master?

--Jonathan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,785
Latest member
undedgini

Latest Threads

Top