From: jimad@microsoft.com (Jim Adcock)
Subject: Re: Alternatives to operator.()
Date: 23 Jul 92 02:41:59 GMT
Organization: Microsoft Corporation

In article <Brqq7D.97w@world.std.com> wmm@world.std.com (William M Miller) writes:
|Yes, I've read your "answers" (see, I can use the quote key, too --
|can we agree to stop casting aspersions on the validity of one
|another's mental processes and just deal with the issues
|dispassionately?).  

This would require some understanding and memory retention of my prior
statements on your part so that I do not have to keep repeatedly answering
your same mistakes made over and over again.

|The reason I listed my objections here was to
|demonstrate that none of them were comparative, not to elicit further
|discussion of them.

Yet when your objections are stated in error, then I have to further
discuss them.  Whereas if you read and understood my corrections to
your erroneous objections, and retracted those erroneous objections, 
then we could go on to something more constructive.

|is.  So is "::".  I contend that "." is more like "::" than like "->".

I have addressed this argument previously.  Built-in operator-> means
the same as "(*ptr)." thus if the "." performs no action then *ptr and
ptr-> should have the same action.  BUT, the language already allows
operator*() and operator->() to be overloaded independently, resulting
in DIFFERENT actions.  So, the EXISTING C++ LANGUAGE DEFINITION contradicts
your position.  To make your position defendable, overloadable operator->() 
would have to be removed from the language, leaving operator->() to ALWAYS 
have the built-in equivalence "(*ptr)."  In which case the actions of -> and .
would always be consistent.  BUT, given that operator->() CAN be overloaded,
the operator.() MUST be overloadable too, so that the relationship
"ptr->" <==> "(*ptr)." can be maintained by the programmer.

------

Your mis-statements below simply reflect a fundamental misunderstanding
of the language and how it works:

|	struct X { int i; };
|
|	int i;
|
|	void f() {
|		int i;
|		X x;
|		X* xp;
|		X& xr = x;
|
|		// reference expression
|
|	}
|
|Consider each of the following expressions at the location indicated
|by the comment "reference expression":
|
|	Expression	Interpretation
|	----------	--------------
|
|	i		Fetch the value at the appropriate offset in
|			f()'s stack frame

Plain Wrong.  The expression "i" at this location means "reference to the object
named by i".  Your interpretation is simply in error, as can be seen
by using your expression "i" as follows:

	i = 5;

If "i" means "Fetch the value...." then one value is being assigned to
another value, which simply does not make sense.  Under your interpretation
then, "i = 5" would have to be a compiler error.

|	::i		Fetch the value at the appropriate offset in
|			the program's static storage region

Plain Wrong.  The expression "::i" at this location means "reference to the 
object named by ::i".  Your interpretation is simply in error, as can be seen
by using your expression "::i" as follows:

	::i = 5;

If "::i" means "Fetch the value...." then one value is being assigned to
another value, which simply does not make sense.  Under your interpretation
then, "::i = 5" would have to be a compiler error.

|	x.i		Fetch the value at the appropriate offset in
|			the object "x"

Plain Wrong.  The expression "x.i" at this location means "reference to the 
object named by 'x.i'".  Your interpretation is simply in error, as can be seen
by using your expression "x.i" as follows:

	x.i = 5;

If "x.i" means "Fetch the value...." then one value is being assigned to
another value, which simply does not make sense.  Under your interpretation
then, "x.i = 5" would have to be a compiler error.

LIKEWISE the expression "x" must itself be a reference.  "." applied
to this reference "dereferences" it to get to the specified referenced
member.

|	(*xp).i		Fetch the value at the appropriate offset in
|			the object found by indirecting through the
|			value of "xp"

Wrong, same reasons.

|	xp->i		Fetch the value at the appropriate offset in
|			the object found by indirecting through the
|			value of "xp"

Wrong, same reasons.

|	xr.i		Fetch the value at the appropriate offset in
|			the object to which "xr" refers, i.e., "x"

Wrong, same reasons.

|Notice that each of these interpretations begins with the same phrase,
|regardless of the presence or absence of "." in the expression.  

Agreed, its just that in every case you use the wrong phrase, and
thereby in every case come to the wrong conclusions.  Whereas if you
understood the language correctly, you would come to the right conclusions.

|The only difference is the context in which the offset is applied.  In two
|of the cases, (*xp).i and xp->i, there is an explicit indirection,
|which is the overloadable operation.  In the case of "xr.i" there is
|deliberate ambiguity as to whether an indirection occurs; a reference
|may be, but need not be, implemented by a pointer.

On the contrary, if your phrases above were correct, you would find
that in all cases an indirection occurs.  Its just that in some built-in
cases the compiler is smart enough to commonly optimize out the indirection.

|The point of this exercise was to demonstrate that, semantically, the
|builtin "." only provides qualification for the right hand side --
|i.e., an addressing context and a scope in which the right hand side
|can be looked up to determine "the appropriate offset" for the
|interpretation.  This is exactly what "::" -- or even the absence of
|qualification -- does.  I don't imagine this analysis will convince
|you. 

It can't convince me because quite simply it is in error, as noted above.

|but perhaps you can at least acknowledge that this interpretation
|of builtin "." is simple and self-consistent. 

It is simple -- simple and erroneous.  It is not consistent, because
it differs with the actual behavior of the language.  Again, when the
language sees an expression "i" it must interpret this expressions as
"reference to an object".  The language cannot use different
interpretations of the expression "i" depending on LHS verses RHS context
because expressions in C/C++ do not depend on context.

| To my mind, this "objection" -- that "." isn't like the overloadable 
|operators -- still stands.

Correct your erroneous mind's understanding of the language, then your erroneous
"objection" falls too.

|> My operator.() proposal DOES NOT require forwarding functions unless
|> the implementor of the reference class DESIRES to use forwarding functions.
|
|But the same can be said of the current situation -- you only have to
|provide forwarding functions for those features of the target class
|you desire to use.  The "objection" still stands.

You can say this -- because as time has show there is no way I can prevent
you from saying erroneous statements.  Your statement is false.  One 
DOES NOT have to provide forwarding functions for those features of the 
target class one desires to use.  On the Contrary one can CHOOSE to provide
forwarding functions if desired.  If NOT desired, then operator.() can
be used to forward enmasse the target class functions requiring a target
class object as an LHS, and "operator targetclass()" can be used for
forward enmasse the target class functionality not requiring a target
class object as an LHS, and within the Smart Ref class no forwarding
functions are necessary because of implied this.

Correct your errors and your objections do not continue to stand.

|This is the same sort of semantic dancing as the previous point; the
|fact is that, under your proposal, you must use a circumlocution to
|access members of the smart reference object.  The "objection" still
|stands.

Your statement is false again.  No circumlocution is necessary because
of "implied this."  Correct your errors and your objection does not stand.

|Thanks for your concern; my facts are in pretty good shape.

If "facts" need not have any basis in reality, then they are indeed in
"pretty good shape" because there is no way that such "facts" can be
challenged.

|That's not what I said.  What I said was that the two alternatives I
|mentioned did not have these three particular disadvantages of your
|proposal but that I had not fully explored them.  

You also need to explore my proposal, so that you can discover that in
fact the particular disadvantages you mention are not in fact disadvantages
of my proposal, but rather errors in understanding on your part.

|They may, in fact,
|have far worse disadvantages than your proposal 

The most common disadvantage of these "other" approaches people have 
"thought" about is that simply they have no basis in the reality of
the existing language.  If one understands the language, and tries
to write down how most of these "thoughts" would work, you simply find
that they cannot work, given the context of the existing language, and
how that existing language works.  Languages and compilers aren't magic, 
"thoughts" people think up have to be implementable in the current context
of how the language and compilers work.  My proposal at least has the
advantage that it can be so implemented.
Newsgroups: comp.std.c++