From: jimad@microsoft.com (Jim Adcock)
Subject: Re: Alternatives to operator.()
Date: 27 Jul 92 20:48:20 GMT
Organization: Microsoft Corporation

In article <BruME9.HGJ@world.std.com> wmm@world.std.com (William M Miller) writes:
|I wonder if it ever occurred to you that the explanation for my
|continuing to maintain my objections is that we disagree about the
|validity of your arguments, rather than that I have forgotten them or
|didn't understand them in the first place?

I considered such a possibility but I had to reject this hypothesis in
the face of your continued misstatements about my proposal.  If you
understood my proposal, you would not make misstatements about it, but
rather would argue against it using true statements.

|expr		addressing context	scope
|----		------------------	-----
|i		stack frame of f()	f()
|
|::i		program static		file
|		storage
|
|x.i		x			class X
|
|(*xp).i		object to which xp	class X
|		points (indirection)
|
|xp->i		object to which xp	class X
|		points (indirection)
|
|xr.i		object to which xr	class X
|		refers (may not be
|		an indirection,
|		depending on
|		implementation)
|
|In each case, you look up the name "i" in the implied scope, take the
|corresponding offset out of the symbol table, and add it to the base
|address implied by the addressing context.  

Your statement is incorrect in both the (*xp) and xp-> cases above,
therefore how can it be an argument against operator.() ?

I keep making this point, and you keep ignoring it:

The language ALREADY permits operators *, &, and -> to be overloaded, yet
you keep making arguments pretending that these overloadable operators
don't ALREADY exist.  You insist that operator.() must be a simple
binding.  Yet this statement is already false within the language.
If your statement were true, then the following two expressions would
have to be identical:

	(*pfoo).doSomething();

	pfoo -> doSomething();

yet the language ALREADY permits such a statement NOT to be true.
Dot therefore, already DOES NOT represent "just" a simple binding,
because overloadable operators &, *, and -> ALREADY "break" this 
statement.  ON THE CONTRARY, then, we need overloadable operator.()
to ALLOW such relationships to continue to be made, in the face of
ALREADY overloadable operators &, * and ->

|Whether you use the
|resulting address in an lvalue or rvalue context is immaterial to the
|process.  The role of "." is simply to indicate that the expression
|preceding the "." is to be used to determine the addressing context
|and scope for the interpretation of the name following.  This is
|exactly analogous to what "::" and the absence of qualification do.

Again, the fact that -> and * can be overloaded INDEPENDENTLY 
ALREADY contradicts your statement, since they already allow 
the historical relationship:

	(*pfoo). 	<==>	pfoo->

to be violated.  On the contrary then, "." must represent the DIFFERENCE
in the operation of (*pfoo) and pfoo-> so that the overall relationship
can be maintained.

|	x.i;
|
|This isn't detailed enough.  Using your terminology, it should read,
|"reference to the object named 'i' within the object 'x'", which is

Wrong.  If this were my terminology, then in the following:

	px -> i;

would mean "i within the object referred to by px"

which is clearly a false statement given that the language ALREADY
allows operator-> to be overloaded.

|Just out of curiosity, did you ever program in Bliss?  If I recall
|correctly, it has this idea that every name is really just a pointer.

No, on the contrary, I program in C++, which has this idea that every
name is really just a reference.

|If the target class has a member operator and you want to use it via
|the smart reference, you MUST provide a forwarding function under your
|proposal.  Neither your operator.() nor the conversion function
|alleviates this necessity.  

I have pointed out that there is a tiny fixed set of unary member operators
where ARM requires the LHS to be a member.  ARM pages 333-337 lists these
as I have previously noted.  The pertinent member functions are as follows, 
as I have previously noted:

1) Assignment:		operator=()
2) Function Call:	operator()()
3) Substcripting:	operator[]()
4) Class Member Access:	operator->()

That's It.  Since there is such a small fixed set of possible functions, 
this does not represent a "real" argument against operator.(), but rather
represents an argument in choice of implementation syntax.  This small
FIXED set of operators could be implemented using templates, or macros,
for example.  This is a very DIFFERENT situation from forwarding member
functions in general, where the names of the member functions cannot be
predicted in advance, and where the set of functions to be forwarded IS NOT
tiny.

Further, as I have previously noted, these set of four functions are exactly
the ones that an implementor of a reference class MIGHT NOT want to have
forwarded!  Thus, NOT having these things forwarded automatically might
argubly be considered a "feature."  IF the smart reference class implementor
CHOOSES to have one or more of these tiny fixed set of member operators
forwarded, that CHOICE is TRIVIAL to implement.

BUT, what if the class implementor CHOOSES NOT to forward one of these 
operators?  What if a "smart reference" implementor CHOOSES that operator=()
should mean -- BY DEFAULT -- assign one smart reference to another
[such as references are implemented in just about all other OOPLS]

	ref1 = ref2;	// assign ref2 to ref1 NOT assign refed-ob2 to refed-ob1

Your position would prohibit such a CHOICE.

|If you "CHOOSE" not to provide such
|forwarding functions, the desired operations simply won't be available
|via the smart reference.  I agree that you have reduced the burden of
|writing forwarding functions in your proposal, but you have not
|eliminated it.

Wherever there is a CHOICE, there is a burden.  Some committee members
would keep *all* such choices from the programmer -- but only for 
operator.() out of all the operators.  Again, if operators *, & and ->
were not ALREADY in the language, I could see this.  BUT operators *,
& and -> ARE ALREADY in the language.  These committee members special
case arguments against ONLY overloading operator.() out of this set
of operators is silly.  The argument has already been decided.  Overloaded
* & -> ARE in the langauge ALREADY, therefor so should overloadable operator.()

|No circumlocution is needed in the smart reference's member functions,
|agreed, but that was not the context I was describing.  If there is a
|"metaoperation" to be performed on the smart reference itself, the
|circumlocation *is* necessary.  Such metaoperations might include
|status queries, cache invalidation, network rerouting, etc.

Again, this represents an implementor CHOICE.  No one forces the programmer
to make these choices, and no one forces the programmer to use operator.()
CHOICES available to the implementor REMAIN te CHOICE to forward each 

function independently, doing with each as they choose, inventing some
new syntax to represent NOT forwarded, etc:

	ref.notForwarded();
	ref->notForwarded()
	local(ref).notForwarded();
	notForwarded(ref);

etc, etc.

These all represent implementor CHOICES.  If the language is to offer
any convenience to the programmer, someone somewhere has to decide that
that convenience IS.  If that convenience fails to meet the needs of some
programmer in some situation, then the programmer needs to be able to
sidestep the "convenience."  You argue that because a language default
doesn't meet the needs of everybody always, we shouldn't offer it to anybody
ever.  This is a vacuous argument.

|I have explored your proposal.  The three disadvantages are still
|there.  Refusing to acknowledge them doesn't make them go away.  There
|is room for discussion about the degree to which they are significant,
|but denial doesn't move that discussion forward very much.

I am happy to acknowledge strengths and weaknesses in my proposal.
However, I will continue to try to refute erroneous statements that you
or any other people make against it.  Again, rather then keep making 
vacuous arguments that "my" proposal is "half-empty" why don't *you*
write down *your* counterproposal for how something, anything else would
work that is better.  Because right now, your counter-proposal is not
"half-empty" -- rather it is totally empty, because you have refused to
counter-suggest any CONCRETE counterproposal, such that you, I or the
other guy can COMPARE two CONCRETE proposals, in order to discover
which might be better.  Again, put up or shut up.  Either come up with
something better, or get out of the way.

I suggest that if you or some other committee member puts in the work to
TRY to come up with a better counterproposal, you will quickly find out
that my proposal isn't nearly as bad as you would suggest.  But you refuse
to address that possibility, by refusing to do any such CONCRETE work.