Topic: Alternatives to operator.()
Author: chased@rbbb.Eng.Sun.COM (David Chase)
Date: 30 Jul 1992 21:45:38 GMT Raw View
In article <Bs6n59.GD@world.std.com> wmm@world.std.com (William M Miller) writes:
>The reason I "ignore" this point is because it is irrelevant to the subject
>of the analysis I was making -- that built-in "." is different from built-in
>"->" because the former does not have a corresponding run-time operation,
>while the latter does. Since the built-in "." has no runtime operation, it
>does not make sense, IMHO, to allow an overloaded operator.(), since
>overloaded operators are primarily runtime operations (calling a function to
>perform some action). The built-in "->" _does_ perform a runtime operation
>(indirection), so providing an overloaded operator->() does make sense from
>this perspective.
I don't want to take any sides in this mudfight, but the preceding
analysis strikes me as particularly bogus. Without looking at the
assembly language, you don't know what "." or "->" do in terms of
run-time operations. In different contexts (in C, ignoring C++) they
do different things -- for instance, "&(X -> a)" is not performing an
indirection. In certain compilation modes (e.g., position independent
code generation) they do different things at run-time. If you're
interpreting the program, they may do other different things at
run-time. From my current point of view (head firmly stuffed into the
back-end of a compiler) it all looks like bits, and (after cfront and
cc and the optimizer get through with them) not very attractive bits.
So, do please try not to confuse the language specification with the
language implementation/compilation. (Didn't I say this once already
in a different context?) If, generally speaking, a proposal isn't a
run-time turkey (a well-defined term, eh?), then it ought to be
evaluated on how well it works with the rest of the language and what
programmers need.
David Chase
Sun
Author: wmm@world.std.com (William M Miller)
Date: Tue, 21 Jul 1992 13:16:25 GMT Raw View
In article <1992Jul20.235728.29058@microsoft.com>, Jim Adcock writes:
> |That was not my intention in posting this message. During our earlier
> |exchange a couple of weeks ago, I posted my list of objections to your
> |proposal. They included, in case you've forgotten:
>
> In case you've forgotten, I've answered these "objections" repeatedly.
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?). The reason I listed my objections here was to
demonstrate that none of them were comparative, not to elicit further
discussion of them.
> a) it IS an operator, and always has been. See for example, K&R 1978
> page 49 lists "->" and "." as BOTH being operators of identical precedence.
I've never said that it wasn't an operator -- syntactically it clearly
is. So is "::". I contend that "." is more like "::" than like "->".
> b) it DOES perform a runtime operation, essentially identical to "->"
> as anyone knows who has looked at the code generated for reference members
> and "." and pointer members and "->"
We interpret the data differently here. The dereferencing code to
which you are referring is the result of the use of a reference in an
expression and not of the appearance of ".". Let me illustrate in the
context of the following example:
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
::i Fetch the value at the appropriate offset in
the program's static storage region
x.i Fetch the value at the appropriate offset in
the object "x"
(*xp).i Fetch the value at the appropriate offset in
the object found by indirecting through the
value of "xp"
xp->i Fetch the value at the appropriate offset in
the object found by indirecting through the
value of "xp"
xr.i Fetch the value at the appropriate offset in
the object to which "xr" refers, i.e., "x"
Notice that each of these interpretations begins with the same phrase,
regardless of the presence or absence of "." in the expression. 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.
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, but perhaps you can at least acknowledge that this interpretation
of builtin "." is simple and self-consistent. To my mind, this
"objection" -- that "." isn't like the overloadable operators -- still
stands.
> | 2) Your operator.() proposal continues to require forwarding functions
> | for member operators and conversion operators in the target class,
> | even though the requirement of forwarding functions was one of the
> | most significant criticisms you advanced against the status quo.
>
> 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.
> | 3) Your operator.() proposal requires use of circumlocutions to access
> | members of the smart reference object.
>
> My proposal DOES NOT require use of circumlocutions to access members of
> the smart reference object. Circumlocations are POSSIBLE, if the implementor
> of the reference class so desires.
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.
> |My position on
> |your proposal is based on these points, not on a comparison with some
> |semi-mythical "better proposal."
>
> Then I suggest:
>
> 1) get your facts right, lest your position be based on errors.
Thanks for your concern; my facts are in pretty good shape.
> 2) you stop saying your ideas are "better" than my proposal
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. They may, in fact,
have far worse disadvantages than your proposal (indeed, as I pointed
out in my most recent posting, the reference-conversion idea isn't
even completely free of these disadvantages, since it requires
forwarding functions for conversion functions in the target). That's
why I posted, to get people to shoot holes in the ideas. It would
definitely be premature to claim that either is "better" than your
proposal, and I haven't done so.
-- William M. Miller, wmm@world.std.com
Author: mat@mole-end
Date: Tue, 21 Jul 1992 09:47:10 GMT Raw View
In article <1992Jul20.235728.29058@microsoft.com>, jimad@microsoft.com (Jim Adcock) writes:
> In case you've forgotten, I've answered these "objections" repeatedly.
> | 2) Your operator.() proposal continues to require forwarding functions
> | for member operators and conversion operators in the target class,
> | even though the requirement of forwarding functions was one of the
> | most significant criticisms you advanced against the status quo.
> My operator.() proposal DOES NOT require forwarding functions unless
> the implementor of the reference class DESIRES to use forwarding functions.
> The functions that the implementor of the reference class might *desire*
> to implement are those small set of functions that ARM currently requires
> the LHS to be a member, . . .
> | 3) Your operator.() proposal requires use of circumlocutions to access
> | members of the smart reference object.
>
> My proposal DOES NOT require use of circumlocutions to access members of
> the smart reference object. Circumlocations are POSSIBLE, if the implementor
> of the reference class so desires. . . .
Perhaps concrete examples might make these points clearer? I know they
take more time than stating the points, and often take up many lines, but
they have a marvellous way of clearing things up.
--
(This man's opinions are his own.)
From mole-end Mark Terribile
uunet!mole-end!mat, Somewhere in Matawan, NJ
Author: jimad@microsoft.com (Jim Adcock)
Date: 23 Jul 92 02:41:59 GMT Raw View
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.
Author: wmm@world.std.com (William M Miller)
Date: Thu, 23 Jul 1992 15:44:31 GMT Raw View
In article <1992Jul23.024159.18039@microsoft.com>, Jim Adcock writes:
> 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.
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 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;
But that wasn't the expression I was explaining. Implicit in my
explanation was an "rvalue" context, which I used because I thought it
would be clearer; in retrospect, I suppose that I should have written
"i;" to avoid any confusion. However, following your suggestion and
using the more general "reference to..." phraseology does not change
the point I was making -- that the "." simply supplies the
qualification needed to interpret what follows it.
Let me recast the table in a way that I hope will avoid any confusion
about lvalues versus rvalues:
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. 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.
> | 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'".
This isn't detailed enough. Using your terminology, it should read,
"reference to the object named 'i' within the object 'x'", which is
consistent with my explanation above.
> 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.
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.
> 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 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. 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.
> Your statement is false again. No circumlocution is necessary because
> of "implied this." Correct your errors and your objection does not stand.
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.
> 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.
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.
-- William M. Miller, wmm@world.std.com
Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Thu, 23 Jul 1992 18:34:54 GMT Raw View
wmm@world.std.com (William M Miller) writes:
>In article <1992Jul23.024159.18039@microsoft.com>, Jim Adcock writes:
>> No circumlocution is necessary because of "implied this."
>
>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.
True.
However in general the idea with smart references is that they be "just like"
real references, so that most of the code doesn't need to tell the difference.
One would expect therefore that most of the code would not need to access
such meta-operations.
There are also several ways of hiding away the circumlocution, if it still
bothers you (variations on the usual macro-hackery). One way would be to
use a functional syntax for the meta-operations:
SmartRef r;
invalidate_cache(r);
query_status(r);
Alternatively you could define one function on smartrefs that returns
a smartref-controller:
Smartref r;
control(r).invalidate_cache();
control(r).query_status();
This looks like a nice way of doing it to me, since it visually flags
the use of meta-operations, while still allowing the normal "." syntax.
[I've left the implementation details unstated; perhaps someone with a little
time to spare might like to fill them in.]
--
Fergus Henderson fjh@munta.cs.mu.OZ.AU
This .signature VIRUS is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
.signature file!
Author: wmm@world.std.com (William M Miller)
Date: Fri, 24 Jul 1992 01:03:10 GMT Raw View
In article <9220604.6372@mulga.cs.mu.OZ.AU>, Fergus James HENDERSON writes:
> There are also several ways of hiding away the circumlocution, if it still
> bothers you (variations on the usual macro-hackery).
Yes, I think both of these are better than "(&r)->query_status()";
however, they are both still circumlocutions.
This may be an opportune time to point out that I have never claimed
that any of my three objections was a "killer," i.e., something that
made the proposal completely unacceptable; they are just points that
have to be weighed against the obvious attractions of the proposal. I
think that posts like this, which reduce the negative impact of the
problems, are exactly the kind of helpful contributions I would hope
to see from the proposal's supporters.
-- William M. Miller, wmm@world.std.com
Author: fjh@mundil.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Fri, 24 Jul 1992 04:37:40 GMT Raw View
To expand a little on my previous post:
>Alternatively you could define one function on smartrefs that returns
>a smartref-controller:
>
> Smartref r;
> control(r).invalidate_cache();
> control(r).query_status();
>
>This looks like a nice way of doing it to me, since it visually flags
>the use of meta-operations, while still allowing the normal "." syntax.
The more I think about this the more I like it.
In fact I would say that is is *better* than just allowing
r.invalidate_cache();
since you *want* meta-operations to stand out as being semantically different
to standard operations. This method allows you to clearly seperate the two.
Also in general you might want to control access to meta-operations, and
having this seperation also makes that easier.
--
Fergus Henderson fjh@munta.cs.mu.OZ.AU
This .signature VIRUS is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
.signature file!
Author: cok@sunshine.Kodak.COM (David Cok)
Date: 24 Jul 92 19:03:21 GMT Raw View
In article <9220614.4939@mulga.cs.mu.OZ.AU> fjh@mundil.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
>
>The more I think about this the more I like it.
>In fact I would say that is is *better* than just allowing
> r.invalidate_cache();
>since you *want* meta-operations to stand out as being semantically different
>to standard operations. This method allows you to clearly seperate the two.
>Also in general you might want to control access to meta-operations, and
>having this seperation also makes that easier.
>
>--
>Fergus Henderson fjh@munta.cs.mu.OZ.AU
>This .signature VIRUS is a self-referential statement that is true - but
>you will only be able to consistently believe it if you copy it to your own
>.signature file!
No, I don't want meta-operations to stand out. I want to be able to design
interfaces in which construction, copying, destruction, conversion all happen
silently and automatically where the interface designs them to. The same
would apply to operator. if it were allowed to be overloaded.
David Cok
Eastman Kodak
cok@Kodak.COM
Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Sat, 25 Jul 1992 09:55:03 GMT Raw View
cok@sunshine.Kodak.COM (David Cok) writes:
>In article <9220614.4939@mulga.cs.mu.OZ.AU> fjh@mundil.cs.mu.OZ.AU (Fergus James HENDERSON) writes:
>>
>>The more I think about this the more I like it.
>>In fact I would say that is is *better* than just allowing
>> r.invalidate_cache();
>>since you *want* meta-operations to stand out as being semantically different
>>to standard operations. This method allows you to clearly seperate the two.
>>Also in general you might want to control access to meta-operations, and
>>having this seperation also makes that easier.
>
>No, I don't want meta-operations to stand out. I want to be able to design
>interfaces in which construction, copying, destruction, conversion all happen
>silently and automatically where the interface designs them to. The same
>would apply to operator. if it were allowed to be overloaded.
But these operations *would* be silent and automatic for the SmartRef
class I was thinking of. It's only meta-operations like invalidate_cache()
that would stand out. (Maybe I don't understand what you mean?)
--
Fergus Henderson fjh@munta.cs.mu.OZ.AU
This .signature VIRUS is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
.signature file!
Author: jimad@microsoft.com (Jim Adcock)
Date: 27 Jul 92 20:48:20 GMT Raw View
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.
Author: wmm@world.std.com (William M Miller)
Date: 30 Jul 92 03:31:56 GMT Raw View
> |expr addressing context scope
> |---- ------------------ -----
> |(*xp).i object to which xp class X
> | points (indirection)
> |
> |xp->i object to which xp class X
> | points (indirection)
> |
> |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,
So you assert, but I saw no argument to that effect in your posting.
> 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.
The reason I "ignore" this point is because it is irrelevant to the subject
of the analysis I was making -- that built-in "." is different from built-in
"->" because the former does not have a corresponding run-time operation,
while the latter does. Since the built-in "." has no runtime operation, it
does not make sense, IMHO, to allow an overloaded operator.(), since
overloaded operators are primarily runtime operations (calling a function to
perform some action). The built-in "->" _does_ perform a runtime operation
(indirection), so providing an overloaded operator->() does make sense from
this perspective.
> we need overloadable operator.()
> to ALLOW such relationships to continue to be made, in the face of
> ALREADY overloadable operators &, * and ->
This is an unjustified conclusion. What is needed is smart references,
whether provided by operator.() or some other method.
> Wrong. If this were my terminology, then in the following:
>
> px -> i;
>
> would mean "i within the object referred to by px"
It does.
> which is clearly a false statement given that the language ALREADY
> allows operator-> to be overloaded.
The action of an overloaded operator->() is irrelevant to this analysis of
the meaning of the builtin operators.
> I have pointed out that there is a tiny fixed set of unary member operators
> where ARM requires the LHS to be a member.
And those are only the beginning of the operators that may require
forwarding functions. In addition, all the compound assignment operators
(+=, -=, etc.) are generally best provided as member operators. Other
operators may be included as members as well to prohibit conversions in
operator contexts. (And don't forget conversion operators, too, which must
be members.)
> That's It. Since there is such a small fixed set of possible functions,
No, the set is unlimited because of argument overloading and conversion
targets.
> 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.
No, since my preference is for a solution in which members of the smart
reference are chosen in preference to the target's members. (Actually, the
special rules for operator=() -- generated by the compiler rather than
inherited from a base class -- require that smart reference classes provide
operator=() regardless of which set of semantics is desired, but the point
still applies to the other operators.)
> 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
I freely agree that I have no counterproposal. The reason is not "refusal"
to write one but simple lack of time, both to explore fully the ideas I have
and to write them down comprehensively if the exploration indicates that the
effort of documenting them is warranted. Perhaps in the fall something will
be possible; in the meantime, part of the role of my criticism is to
establish some of the criteria which a proposal would have to fulfill in
order for me to consider it "better."
I continue to emphasize, though, that I'm not arguing against your proposal
in favor of some other proposal but simply explaining the things I don't
like, regardless of whether any other proposal ever comes down the pike.
The issue is not, at the moment at least, whether your proposal is better
than some other, but whether it's good enough. Is your proposal better than
the status quo? Probably. Is it enough better to warrant changing the
language? I'm not convinced, and the reasons are my three objections:
1) It requires warping one's model of the language to attribute a runtime
action to "." (to be fair, some people have apparently already acquired this
warped view, so it's not a disadvantage for them :-)
2) It leaves a bunch of forwarding functions still potentially required
3) It requires doing something unobvious to access members of the smart
reference (and, as has been pointed out, there are lots of ways of doing
that, none obviously better than the others).
The degree of importance one assigns to these issues in comparison to the
benefits of the proposal is obviously a value judgment, but they do have to
be considered.
Author: checker@acf3.nyu.edu (Christopher Hecker)
Date: Fri, 17 Jul 1992 22:18:30 GMT Raw View
> void f() {
> Ref r;
> r.memRef = 1; // r.Ref::memRef
> r.memT = 2; // (r.operator T&()).memT
> }
>Both of these approaches have the advantages listed above; however, I'm sure
>there are lots of other things to consider about them. I'm not supporting
>either at the moment, just soliciting questions and opinions. Comments?
Hmm, when we were talking about the Adcock proposal on BIX I mentioned
this thinking that it had been mentioned already in one of Jim's papers.
I'm sure I saw it around here somewhere a couple of weeks ago. Anyway,
how would you limit the type of conversions allowable on the lhs of the
`.'? Would this be legal:
class string
{
public:
string(char const *);
string operator+(string const &);
string &operator=(string const &);
};
string Foo;
Foo = "a string" + Foo;
Currently, operator+ is best defined as a friend function because that
allows you to express the fact that either operand can be an rvalue,
whereas a member function requires the lhs to be an lvalue.
Chris
Author: jimad@microsoft.com (Jim Adcock)
Date: 17 Jul 92 21:43:37 GMT Raw View
In article <1992Jul16.081436.4623@ericsson.se> jonas@beppe.ericsson.se (Jonas Nygren) writes:
|When I started to look at gcc to understand what it would take to
|implement this feature I got some second thoughts. Why do we want
|smart pointers/references? The only reason I see is that it allows
|you to implement some sort of GC based on reference counters. Are there
|other nice uses of smart pointers?
Other uses of smart pointers include range checking, null checking,
swizzling/deswizzling references to large secondary store, access to
objects in other memory spaces or on other machines, implementing
"pointers" with LESS functionality than the multitude of different
functionalities that C/C++ pointer support, implementing "pointers"
with MORE or DIFFERENT functionality than C/C++ pointers support, etc, etc.
"Smart References" have a similarly huge range of uses. Further, Smart
References offer a much more attactive set of syntaxes for doing
"value oriented" programming, such as when one is doing numerical work.
|If only used for GC wouldn't it be better to add GC to the language?
It would be better to add GC to the language in any case. However, if
it takes more than two years to change the language one iota, it would
take an eternity to get GC included. Still, "Smart References" are a
very important building block to people that would experiment with GC
in c++ today, people who would be building the foundation on which to
implement fast, efficient, easy-to-use GC supported by the language,
at some day in the future.
Author: jimad@microsoft.com (Jim Adcock)
Date: 17 Jul 92 21:33:35 GMT Raw View
In article <BrGIF7.IvE@world.std.com> wmm@world.std.com (William M Miller) writes:
|When I was discussing Jim Adcock's proposal earlier, I mentioned that I was
|looking at ways of providing smart references that were more comprehensive
|than operator.(). One possibility was to resurrect Bjarne's idea of
|delegation via pointer, presented in his original multiple inheritance paper
|given at the 1987 EUUG meeting.
I have repeatedly ask committee members for copies of any papers relating
to alternatives to my operator.() proposal. No such alternatives have
been shown to me.
|The advantages of that kind of approach over Jim's operator.() (to
|distinguish it from the alternative operator.() formulations mentioned in
|the Koenig/Stroustrup paper, since they are more like the pointer delegation
|idea in these points) are threefold:
I have repeatedly ask committee members for copies of any papers relating
to alternatives to my operator.() proposal. No such alternatives have
been shown to me.
As far as I can tell, the main advantages of these other proposals is that
no one has bothered to think them through and write them down. If people
do attempt to think some of these ideas through and Write Them Down, then
I claim they will find out that many of these ideas Simply Don't Work.
For example, many people keep suggesting that operator.() should be selectively
invoked based on context. If they bothered to think these ideas through
and attempt to write them down as modifications to the existing standard,
then they would discover that their proposals really involve BINARY operator.()
which in turn requires "references to members" -- which the language Simply
Doesn't Have.
Again, claims that this that or the other idea is "better" before someone
Writes Them Down are simply totally vacuous claims. Write Them Down and
we will compare them side to side, see what is better about what, what is
worse than what, what works, and what doesn't work. CLAIMS that something
unspecified is better than something specified are completely vacuous --
no one can or could put any value on such claims until that which is claimed
is specified. Otherwise you could be talking about anything or nothing --
it is impossible to know or to discuss *which* !!!
|1) it allows target-class operators to be used via the smart reference
|without forwarding functions
PROVE IT.
|2) it allows access to members of the smart reference itself without
|circumlocutions
PROVE IT.
|3) it doesn't require thinking of "." as an executable operator.
PROVE IT. You have shown NOTHING. All you have done is make vacuous
claims. Post a SPECIFIC PROPOSAL here of what changes you are suggesting
to the spec, and THEN we can discuss these issues.
|Both of these approaches have the advantages listed above; however, I'm sure
|there are lots of other things to consider about them. I'm not supporting
|either at the moment, just soliciting questions and opinions. Comments?
If you want to have such ideas considered, then write them up in detail
enough that people can understand what you are talking about, and could
actually implement a compiler based on your ideas. Make sure your ideas
specify *exactly* what changes are to be made to the spec and the exact
language to be used in those changes. Unless you are willing to do this
work you are just blowing smoke.
Many people are unhappy about my proposal, but it does have certain advantages
to the ideas you suggest here:
1) Its a written proposal that documents EXACTLY the required changes in
the language spec.
2) It can actually be implemented.
3) Its a loca ilized change in the language
4) Its consistent with the existing features in the language,
namely unary operator->, unary operator*, and unary operator&
5) Its thought out, thought through, and written down.
I don't claim my proposal is the "best possible" proposal -- I simply
state that no one has been able or willing to show me a better proposal.
If you want something better -- then Write It Down -- and YOU convince
committee members to adopt your idea. If your idea does what my proposal
does and does it better, then I will support your ideas if you can get
the committee to adopt your ideas. But please don't continue to use
vacuous claims about ideas that cannot or have not been written down
as excuses to dismiss my proposal which Has been written down.
Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Sat, 18 Jul 1992 08:10:57 GMT Raw View
In article <1992Jul17.213335.19671@microsoft.com> jimad@microsoft.com (Jim Adcock) writes:
>In article <BrGIF7.IvE@world.std.com> wmm@world.std.com (William M Miller) writes:
>|When I was discussing Jim Adcock's proposal earlier, I mentioned that I was
>|looking at ways of providing smart references that were more comprehensive
>|than operator.(). One possibility was to resurrect Bjarne's idea of
>|delegation via pointer, presented in his original multiple inheritance paper
>|given at the 1987 EUUG meeting.
>
>I have repeatedly ask committee members for copies of any papers relating
>to alternatives to my operator.() proposal. No such alternatives have
>been shown to me.
Did you miss what I saw Jim?
Bjarne had a proposal like this:
class X : *p { Y *p; ...
and a call
X x;
x.fred();
if not satisfied by a class X member is delgated
to class Y via the pointer p:
X.p->fred();
OK: thats a brief spec. Bjarne did much more than write it down,
he *implemented* it on a test compiler and had programmers use it.
I understand a problem was the programmers expected that
the functions of Y could be overridden in X somehow.
>As far as I can tell, the main advantages of these other proposals is that
>no one has bothered to think them through and write them down.
No, the main problem is that they didn't tell YOU about it.
Or ME for that matter. I sure would like to know more about what they
are thinking about so I can learn and maybe even help.
>For example, many people keep suggesting that operator.() should be selectively
>invoked based on context. If they bothered to think these ideas through
>and attempt to write them down as modifications to the existing standard,
>then they would discover that their proposals really involve BINARY operator.()
>which in turn requires "references to members" -- which the language Simply
>Doesn't Have.
If YOU had do that and put it in the proposal, you would have
saved them a lot of needly worry. *I* worried about this until
you explained your proposal in more detail, EVEN THOUGH YOUR
ACTUAL PROPOSAL WAS QUITE SPECIFIC.
[I'm just not smart enough]
>
>|1) it allows target-class operators to be used via the smart reference
>|without forwarding functions
>
>PROVE IT.
nothing to prove: you can see from the spec above that
it works.
>
>|2) it allows access to members of the smart reference itself without
>|circumlocutions
>
>PROVE IT.
As above.
>
>|3) it doesn't require thinking of "." as an executable operator.
>
>PROVE IT.
It is evident.
>You have shown NOTHING. All you have done is make vacuous
>claims. Post a SPECIFIC PROPOSAL here of what changes you are suggesting
>to the spec, and THEN we can discuss these issues.
Well, I have posted MY understanding of it.
>
>Many people are unhappy about my proposal, but it does have certain advantages
>to the ideas you suggest here:
>
>1) Its a written proposal that documents EXACTLY the required changes in
>the language spec.
>
>2) It can actually be implemented.
>
>3) Its a loca ilized change in the language
>
>4) Its consistent with the existing features in the language,
>namely unary operator->, unary operator*, and unary operator&
>
>5) Its thought out, thought through, and written down.
>
And possibly it can be put in the language AS WELL as
the other proposal above. Because your main argument
is SYMMETRY and it is a very strong argument.
I believe that operator.() is not really an extension
at all---mere removal of an interim restriction. If I dont
use the facility, no harm is done to me. If I choose to use it,
I have to use it with care, like any other C++ idiom.
--
;----------------------------------------------------------------------
JOHN (MAX) SKALLER, maxtal@extro.ucc.su.oz.au
Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------
Author: wmm@world.std.com (William M Miller)
Date: 19 Jul 92 01:15:08 GMT Raw View
In article <1992Jul17.221830.16252@cmcl2.nyu.edu>, Christopher Hecker writes:
> Anyway,
> how would you limit the type of conversions allowable on the lhs of the
> `.'? Would this be legal:
>
> class string
> {
> public:
> string(char const *);
> string operator+(string const &);
> string &operator=(string const &);
> };
>
> string Foo;
> Foo = "a string" + Foo;
That's a very good question, just the sort of thing I was hoping people
would bring up. My initial response is that I would not want that to be
legal. The reason is that you would potentially have to look at every class
in scope to see if it contained an operator+() and an appropriate conversion
constructor in order determine if there were a matching operator+() that
could be applied; that's the only place in the language where the left hand
side does not determine the set of scopes that have to be searched to
resolve a member function references.
If the approach I mentioned were adopted, the left hand side would still be
determinative of the search; the scopes to be examined would include the
class of the left hand side, say X, and all classes for which X contained a
conversion-to-reference operator. (It would not require a transitive
closure because of the rule limiting automatic conversions to a single level
of user-defined conversions.)
A related issue is currently under discussion in the Committee: whether it
is legitimate to invoke a member function for a temporary. If the decision
is "no," that would be another reason for rejecting the case you cited.
Thanks for the question!
Author: wmm@world.std.com (William M Miller)
Date: 19 Jul 92 01:17:41 GMT Raw View
In article <1992Jul17.213335.19671@microsoft.com>, Jim Adcock writes:
> |When I was discussing Jim Adcock's proposal earlier, I mentioned that I was
> |looking at ways of providing smart references that were more comprehensive
> |than operator.(). One possibility was to resurrect Bjarne's idea of
> |delegation via pointer, presented in his original multiple inheritance paper
> |given at the 1987 EUUG meeting.
>
> I have repeatedly ask committee members for copies of any papers relating
> to alternatives to my operator.() proposal. No such alternatives have
> been shown to me.
I have no idea whether anyone else on the Committee has ever considered
either of the possible alternatives I presented in my posting (although I
would be surprised if I were the first, I do not recall having ever
discussed them with anyone or seen them written up anywhere. If my memory
has betrayed me and I'm unconsciously plagiarizing, I hope someone will
correct me and I'll gladly acknowledge the other's priority.). Bjarne's
delegation via pointer idea was written up and abandoned long before your
operator.() proposal, so I don't think he was viewing it as a competitor to
your proposal. In any case, I have typed in and posted nearly the entire
section of his paper dealing with delegation via pointer (I omitted a couple
of paragraphs dealing with virtual functions, as I recall, which I didn't
think were germane to smart references), so you've seen everything that has
been written about it, to my knowledge.
> |Both of these approaches have the advantages listed above; however, I'm sure
> |there are lots of other things to consider about them. I'm not supporting
> |either at the moment, just soliciting questions and opinions. Comments?
>
> If you want to have such ideas considered, then write them up in detail
> enough that people can understand what you are talking about, and could
> actually implement a compiler based on your ideas. Make sure your ideas
> specify *exactly* what changes are to be made to the spec and the exact
> language to be used in those changes. Unless you are willing to do this
> work you are just blowing smoke.
As I thought I made clear in my posting, both of these ideas are in their
infancy, at least regarding my own consideration of them. I've probably
spent maybe a half hour total between them just noodling around. They are
certainly not yet at the stage for me to spend the many hours required to
create a submit-it-to-the-Committee-quality proposal, and in fact they may
never be. The specific reason I posted them was so that people could point
out problems I hadn't seen and ask questions that hadn't occurred to me. If
the problems are too many or too serious, there will never be such a
proposal and I will have saved a lot of my time and that of the Committee.
> please don't continue to use
> vacuous claims about ideas that cannot or have not been written down
> as excuses to dismiss my proposal which Has been written down.
That was not my intention in posting this message. During our earlier
exchange a couple of weeks ago, I posted my list of objections to your
proposal. They included, in case you've forgotten:
1) "." isn't an operator like the currently-overloadable operators
because it performs no runtime operation, it just supplies
qualification for the right hand side, like "::".
2) Your operator.() proposal continues to require forwarding functions
for member operators and conversion operators in the target class,
even though the requirement of forwarding functions was one of the
most significant criticisms you advanced against the status quo.
3) Your operator.() proposal requires use of circumlocutions to access
members of the smart reference object.
These objections are completely independent of the existence or nonexistence
of a "better proposal." (I should note in the interest of honesty that
objection #2 also applies in part to my second idea, the one allowing use of
conversion-to-reference to forward member access, since it also would
require forwarding functions for conversion operators in the target class.
I only just realized that when I was writing the above.) My position on
your proposal is based on these points, not on a comparison with some
semi-mythical "better proposal."
Author: jimad@microsoft.com (Jim Adcock)
Date: 20 Jul 92 23:28:15 GMT Raw View
In article <BrM3H9.C43@world.std.com> wmm@world.std.com (William M Miller) writes:
|A related issue is currently under discussion in the Committee: whether it
|is legitimate to invoke a member function for a temporary. If the decision
|is "no," that would be another reason for rejecting the case you cited.
sigh. If it is not legitimate to invoke a member function for a temporary,
then how, prey tell, does one make the temporary in the first place?
Author: jimad@microsoft.com (Jim Adcock)
Date: 20 Jul 92 23:57:28 GMT Raw View
In article <BrM3LI.CBo@world.std.com> wmm@world.std.com (William M Miller) writes:
|That was not my intention in posting this message. During our earlier
|exchange a couple of weeks ago, I posted my list of objections to your
|proposal. They included, in case you've forgotten:
In case you've forgotten, I've answered these "objections" repeatedly.
| 1) "." isn't an operator like the currently-overloadable operators
| because it performs no runtime operation, it just supplies
| qualification for the right hand side, like "::".
"." IS an operator like the currently-overloadable operators BECAUSE:
a) it IS an operator, and always has been. See for example, K&R 1978
page 49 lists "->" and "." as BOTH being operators of identical precedence.
b) it DOES perform a runtime operation, essentially identical to "->"
as anyone knows who has looked at the code generated for reference members
and "." and pointer members and "->"
| 2) Your operator.() proposal continues to require forwarding functions
| for member operators and conversion operators in the target class,
| even though the requirement of forwarding functions was one of the
| most significant criticisms you advanced against the status quo.
My operator.() proposal DOES NOT require forwarding functions unless
the implementor of the reference class DESIRES to use forwarding functions.
The functions that the implementor of the reference class might *desire*
to implement are those small set of functions that ARM currently requires
the LHS to be a member, these functions are listed in ARM 13.4.3, 13.4.4,
and 13.4.5, namely "=", "()" and "[]". "()" would not be commonly used.
"[]" might be fairly commonly used, but likewise it might be commonly
desired to do something special in implementing it. And If "=" were
AUTOMATICALLY implemented for people trying to write reference classes,
as you suggest, then it would be impossible for anyone to write a re-assignable
reference class -- such as all the OOPLs support -- if the reference class
implementor so desired. So I'd argue contrary that the fact that these
few operators aren't "AUTOMATICALLY" implemented represents an ADVANTAGE,
not a DISADVANTAGE!
| 3) Your operator.() proposal requires use of circumlocutions to access
| members of the smart reference object.
My proposal DOES NOT require use of circumlocutions to access members of
the smart reference object. Circumlocations are POSSIBLE, if the implementor
of the reference class so desires. Where such "tricks" might be useful
might be in debugging the "smart reference class" code. Note that C++
PREVENTS the ability of any such debugging capabilities on the built-in
references! Contrarwise, there is no NEED for the implementor of the
"smart reference" class to implement such circumlocations when NOT desired,
because within the implementation of the reference class, members are
access using "implied this" making use of "." unnecessary.
|My position on
|your proposal is based on these points, not on a comparison with some
|semi-mythical "better proposal."
Then I suggest:
1) get your facts right, lest your position be based on errors.
2) you stop saying your ideas are "better" than my proposal, since neither
you nor I nor anyone can say which approach is "better" when comparing
comparing something specific to something "mythical." If you say that
inheriting via pointer *might* be a better approach to "smart references"
then I might agree with you. Write up specifics, and then you I and the
other guy can point out the strengths and disadvantages of ALL the various
proposals. But I am tired of people saying that this that or the other
mythical approach is "better" than my suggestion. Could be, may be,
may not be. If you think you ideas have merit, you write them up, and
then YOU defend them.
Author: wmm@world.std.com (William M Miller)
Date: Thu, 16 Jul 1992 00:52:18 GMT Raw View
When I was discussing Jim Adcock's proposal earlier, I mentioned that I was
looking at ways of providing smart references that were more comprehensive
than operator.(). One possibility was to resurrect Bjarne's idea of
delegation via pointer, presented in his original multiple inheritance paper
given at the 1987 EUUG meeting.
The advantages of that kind of approach over Jim's operator.() (to
distinguish it from the alternative operator.() formulations mentioned in
the Koenig/Stroustrup paper, since they are more like the pointer delegation
idea in these points) are threefold:
1) it allows target-class operators to be used via the smart reference
without forwarding functions
2) it allows access to members of the smart reference itself without
circumlocutions
3) it doesn't require thinking of "." as an executable operator.
Since these were the major points I didn't like about Jim's proposal, I
originally found this approach to be quite attractive. As I have thought
further, however, I have discovered a substantial drawback to using
delegation via pointer, at least as defined in Bjarne's original proposal,
as the mechanism for smart references: although the pointer can be changed
at any time, there is no function that is invoked when the reference is
used. That means that delegation-via-pointer smart references couldn't be
used for many of the applications in which operator->() is currently
employed, such as transparent virtual memory mapping, metering frequency of
access, etc.
Two ideas have occurred to me to remedy these deficiencies while still
providing the benefits listed above. One possibility is to extend the
delegation-via-pointer concept to include not only member and global
pointer data but also member or standalone function return values. For
example,
class foo: *bar() {
...
};
As in the original proposal, bar() could be either a member or global
function, returning a pointer to a class. The names declared in the class
to which bar() returns a pointer would be added to class foo in the same way
base class names are added. Thus, any member reference involving an object
of class foo would resolve to a member of class foo if there was such a
member, otherwise the reference would resolve to a member of the class to
which bar() returned a pointer. bar() would be invoked to obtain the "this"
used to qualify the latter kind of member references but not for the ones
resolved to members of class foo.
Another possibility would involve a minor extension of the semantics of the
language, no change whatsoever to the syntax. As Jim pointed out, the
reason smart references aren't available in the current language is the
restriction that the type of the expression to the left of the "." must be
that of a class containing a declaration of the member referenced on the
right of the ".". If this restriction were lifted, smart references could
be implemented by a conversion operator.
To be more specific, consider the following:
class T {
public:
int memT;
};
class Ref {
public:
operator T&();
int memRef;
};
void f() {
Ref r;
r.memRef = 1; // r.Ref::memRef
r.memT = 2; // (r.operator T&()).memT
}
At first blush, this approach seems to be consistent with the way member
function and operator overloading is resolved -- the "this" pointer is
considered just like an additional argument, with an exact match beating a
conversion. In the same way, a member of class Ref would be an exact match
on "this," while a member of class T would require a user-defined conversion
for the match.
Both of these approaches have the advantages listed above; however, I'm sure
there are lots of other things to consider about them. I'm not supporting
either at the moment, just soliciting questions and opinions. Comments?
Author: jonas@beppe.ericsson.se (Jonas Nygren)
Date: Thu, 16 Jul 1992 08:14:36 GMT Raw View
In article <BrGIF7.IvE@world.std.com> wmm@world.std.com (William M Miller) writes:
>When I was discussing Jim Adcock's proposal earlier, I mentioned that I was
>looking at ways of providing smart references that were more comprehensive
>than operator.(). One possibility was to resurrect Bjarne's idea of
deleted text
>
>Two ideas have occurred to me to remedy these deficiencies while still
firs idea deleted
>
>Another possibility would involve a minor extension of the semantics of the
>language, no change whatsoever to the syntax. As Jim pointed out, the
>reason smart references aren't available in the current language is the
>restriction that the type of the expression to the left of the "." must be
>that of a class containing a declaration of the member referenced on the
>right of the ".". If this restriction were lifted, smart references could
>be implemented by a conversion operator.
>
>To be more specific, consider the following:
>
> class T {
> public:
> int memT;
> };
>
> class Ref {
> public:
> operator T&();
> int memRef;
> };
>
> void f() {
> Ref r;
> r.memRef = 1; // r.Ref::memRef
> r.memT = 2; // (r.operator T&()).memT
> }
>
>At first blush, this approach seems to be consistent with the way member
>function and operator overloading is resolved -- the "this" pointer is
>considered just like an additional argument, with an exact match beating a
>conversion. In the same way, a member of class Ref would be an exact match
>on "this," while a member of class T would require a user-defined conversion
>for the match.
>
>Both of these approaches have the advantages listed above; however, I'm sure
>there are lots of other things to consider about them. I'm not supporting
>either at the moment, just soliciting questions and opinions. Comments?
>
I have had the same idea an find it very attractive! The good thing
is that it wont break any existing code and is also quite easy to use.
When I started to look at gcc to understand what it would take to
implement this feature I got some second thoughts. Why do we want
smart pointers/references? The only reason I see is that it allows
you to implement some sort of GC based on reference counters. Are there
other nice uses of smart pointers?
If only used for GC wouldn't it be better to add GC to the language?
/jonas