[net.lang.c] references - C++

bs@alice.UucP (Bjarne Stroustrup) (09/06/86)

Karl W. Z. Heuer writes:

> BMS-AT!stuart writes:
> >[re the declaration of reference types "foo(char &c)"]
> >I don't like this.  It violates the nice consistent way that C expressions
> >work.  'char *c' means that '*c' is of type char.  '&c' is not of type
> >char in any other context.
>
> I am also somewhat uneasy about calling it "char &c".  The consistent way to
> declare it would be "char *&c", since you have to write "&c" to get ahold of
> the "char *" object you are really using.

The notation T& is the C++ notation for a reference to an object of type T.
	char& c;	declares c to be a reference to a char
	char*& r;	declares r to be a reference to a char*
& is a declarator operator exactly like *

ark@alice.UucP (Andrew Koenig) (09/07/86)

Karl W. Z. Heuer writes:

> BMS-AT!stuart writes:
> >[re the declaration of reference types "foo(char &c)"]
> >I don't like this.  It violates the nice consistent way that C expressions
> >work.  'char *c' means that '*c' is of type char.  '&c' is not of type
> >char in any other context.
>
> I am also somewhat uneasy about calling it "char &c".  The consistent way to
> declare it would be "char *&c", since you have to write "&c" to get ahold of
> the "char *" object you are really using.

Bjarne Stroustrup made a comment on this about the way C++ does
things, but I suspect his remark is a little too abbreviated for
people not familiar with C++.  Here is some more detail:

In C++ one can declare a reference using the & declarator operator
in much the same way as one can declare a pointer using the * declarator
operator.  There is one big difference:  a reference MUST always be
initialized at the time it is declared.  Thus, I can write:

	char c;
	char &cr = c;
	char *cp = &c;

The first example declares a character.  No problem there.  The third
declares a character pointer and initializes it to the address of c.
No problem there either.  The second line declares a character reference
named cr and makes it a synonym for c.  Thus, if I write

	*cp = '?';

that sets the value of c to '?'; and so does

	cr = '?';

You can see a few things from this example:

	1. A reference must always be initialized as it is delcared.

	2. Subsequent uses of the reference denote the object with
	   which the reference was initialized.

	3. Therefore, the initializer for a reference must be an lvalue.

In other words:

	char c;
	char &cr = c;		/* legal */
	char &cr;		/* illegal -- no initialization */
	char &cr = &c;		/* illegal -- &c is not an lvalue */

sam@think.COM (Sam Kendall) (09/09/86)

In article <6027@alice.uUCp> ark@alice.UucP (Andrew Koenig) writes:
>	1. A reference must always be initialized as it is delcared.

Except of course for formal arguments, which are initialized by their
actuals.

>	3. Therefore, the initializer for a reference must be an lvalue.

C++ Ref Man 3.6.3: 

    If the initializer for a reference to type T is not an lvalue an
    object of type T will be created and initialized with the
    initializer.  The reference then becomes a name for that object.

This is also the semantics of Fortran argument passing, and I'm sure
people calling Fortran from C++ are very happy -- `sin(2.3)', if `sin'
is a Fortran function, works!

>	char c;
>	char &cr = &c;		/* illegal -- &c is not an lvalue */

The last line is illegal because `&c' has the wrong type, not because
it is an rvalue.

---
Sam Kendall			sam@Think.COM
Thinking Machines Corp.		ihnp4!think!sam

ark@alice.UucP (Andrew Koenig) (09/09/86)

Apparently I caught whatever was affecting Bjarne when he posted his
original note about references.  Sigh.  I'll try again.

Sam Kendall is right -- if you initialize a reference with something
that isn't an lvalue then the reference refers to a temporary.  Thus:

	char c;
	char &cr;		/* illegal -- no initialization */
	char &cr = c;		/* legal */
	char &cr = 'a';		/* equivalent to:  char temp='a'; char &cr=temp; */
	char &cr = &c;		/* illegal -- &c is not a char */

Sam is also right in saying that initializers do not have to be
explicitly provided for references declared as formal parameters.
This is indeed because the initializers will appear whenever the
function is called.  Thus:

	void zap (int& x) { x = 0; }

	int z;
	zap (z);		/* sets z to 0 */

However:

	double d;
	zap (d);		/* illegal -- d is the wrong type */
	zap ((int) d);		/* sets a copy of the integer value
				   of d to 0.  Essentially no effect */

karl@haddock (09/09/86)

alice!bs (Bjarne Stroustrup) writes:
>Karl W. Z. Heuer writes:
>>BMS-AT!stuart writes:
>>>[re the declaration of reference types "foo(char &c)"]
>>>I don't like this.  It violates the nice consistent way ...
>>
>>I am also somewhat uneasy about calling it "char &c".  The consistent way to
>>declare it would be "char *&c", since you have to write "&c" to get ahold of
>>the "char *" object you are really using.
>
>The notation T& is the C++ notation for a reference to an object of type T.
>        char& c;        declares c to be a reference to a char
>        char*& r;       declares r to be a reference to a char*
>& is a declarator operator exactly like *

Understood.  The point was that (in C) a declaration like "int *(*f[])();"
asserts that "*(*f[])()" is an int (so one can work backwards to show that
f must be an array of pointers to functions returning pointers to int).  The
use of "&" for the reference declarator operator breaks this heuristic; now
"char &c" declares a reference to a "char", so "&c" is of type "char *".  If
the operator were spelled "*&" instead, then "char *&c" would denote a
reference to a "char", "char **&r" would be a reference to a "char *", and
"char &x" would be illegal.  Since "**&r" really does give you an object of
type "char", this notation might be easier to interpret.  (I suspect that
stuart was (incorrectly) applying this heuristic, and it led him to believe
that "&c" was somehow an object of type "char".)

Karl W. Z. Heuer (ima!haddock!karl; karl@haddock.isc.com)
This isn't C (not even ANSI proposed), so let's move it to net.lang.c++, OK?