[comp.lang.c] Must casting destroy lvalueness?

throopw@dg_rtp.UUCP (Wayne Throop) (11/09/86)

> leichter@yale.UUCP (Jerry Leichter)

> I can think of no particular use for casting of arbitrary lvalues, but in
> situations as above, the following definition for a cast argument to op=
> would be handy:
>         (type)a op= b
>     shall mean:
>         a = (type)a op b
> (except that a is only evaluated once).

Good idea.  Or rather, "less bad than most such ideas".  I will end up
arguing against this, but it is by far the best possible interpretation
that allows casts as lvalues I've seen.

On the good side, the only real justification for all this hoo-hah about
allowing cast values to be lvalues sometimes is an attempt to evaluate
some expressions only once.  It is therefore quite apt to deal with this
as a generalization of the idea of op= rather than a modification of the
notion of lvalueness.  Also, this idea generalizes to any unary operator
that normally destroys lvalueness, not just casts, for example

        -a += 1;               /* negate a and add 1 */
        sizeof(a) *= 10;       /* assign a peculiar constant to a */

and so on and on.  This (especially the cast cases) would allow a small
number of additional useful cases to get by with only one "actual"
evaluation while multiple "conceptual" evaluations occur.

On the bad side, no matter how you try, there will be some cases where
more than one actual evaluation will be needed to express the conceptual
evaluation one has in mind as a single C expression.  One has to draw
the line somewhere.  C currently draws it at the simplest place: right
at any operator that conceptually produces a distinct new value, such as
casts, negations, sizeofs and the like.  All in all, the standard C
position seems like a very good place to draw this line.

The point is, if somebody says

        ++((type *) p)              or  ((type *)p)++

Jerry's idea says they really mean

        p=((type *)p)+1             or  (q=p,p=((type *)p)+1,q)

The question is, is the shorthand worth it in this particular case?  The
case folks clamor for this is where p is a simple pointer variable
(often a register variable to boot), so the concern surely isn't for the
efficency of the dual evaluation.  And it doesn't seem too likely that
the concern is for clarity, either, since (p=((type *)p)+1) is pretty
clear.  The extra "q" that muddies up the postincrement case isn't even
necessary if the increment is factored out of the single expression, as
it could easily be.

A little more about clarity: why should Jerry's idea be rejected on
grounds of no gained clarity?  After all, the binary abbreviations
implemented op= really help clarity.  Why not extend this to unary
operators also?  My answer: the binary case was indeed a big clarity
win, because such expressions (a += b, for example) more closely matched
how we think of such things... we think of it as "add b to a", not
"assign the sum of a and b to a".  But in the case of ((type *)p) += 1,
there really is a conversion operation on the pointer, then an
increment, then an assignment (and the assignment implies a conversion
back!!!).  Compressing two conversions and an increment into the
assignment in this abbreviation is misleading, and doesn't really
reflect how we think (or how we ought to think) of what is going on.

I expect there are those that disagree with me, and I suppose I
wouldn't be totally aghast if Jerry's idea were adopted by X3J11.
But it just doesn't seem all that necessary, and it is a slightly
misleading syntax.

--
MICHAELMAS: "You don't think my theory holds water?"
DOMINO:     "A bathtub will hold water.  A canteen is usually sufficent."
                                --- from "MICHAELMAS" by Algis Budrys
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw