Topic: Redefinable operator dot? (Was STL and Pointers)


Author: Martin D Kealey <martin@kcbbs.gen.nz>
Date: 1997/05/30
Raw View
In article <5mftao$cm02@iccu9.ipswich.gil.com.au> you wrote:
> herbs@cntc.com (Herb Sutter) wrote:
>> bparker@gil.com.au (Brian Parker) wrote:
>>> Given that such a definition would only require a few lines to add to
>>> the draft standard (as it is almost identical to operator->() ), I
>>> wonder if it really is too late to add it to the draft.

>> ...and that's even if it were really that easy.  Remember that this has been
>> discussed for years, and is a nontrivial design issue.  Others have argued
>> for different operator. semantics than you propose.

> There is no doubt, though, that making operator.() analogous to
> operator->() would be the simplest definition. Given that a
> definition with more complex semantics couldn't be agreed upon, I
> would have preferred to see at least the above semantics rather than
> nothing, though I suppose that this leaves open the option of defining
> operator.() in the next standard revision after more experience has
> been obtained.

I've spent a while thinking up different possible semantics, and since
most of the other reasonable variations tend to result in all sorts of
other things changing depending on whether operator.() is visible or
not, I tend to think that this is the simplest direction: operator.()
always applies where an actual dot occurs in the source code, for a
class that defines the operator, and never applies otherwise.

However, I would add two constraits:

(1) any class which contains an operator.() can't have any usable public
    named (non-operator) members, so they might as well be deemed to
    make the program ill-formed.  (This could be relaxed later, but I
    think this way would minimise the possibility of silent changes if
    operator.() is added to or removed from a class.

    It also can't have any friends, since they can't do anything either.
    (Well, maybe they can if you have private operators, but that would
    be stretching things a bit.)

(2) make operator.() non-inheritable, analogously to operator=().  This
    means that a derived class wouldn't suddenly change its meaning
    because one of its base classes has an operator.() added or removed.

-Martin.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: bparker@gil.com.au (Brian Parker)
Date: 1997/06/02
Raw View
On 30 May 97 15:11:42 GMT, Martin D Kealey <martin@kcbbs.gen.nz>
wrote:
...
>I've spent a while thinking up different possible semantics, and since
>most of the other reasonable variations tend to result in all sorts of
>other things changing depending on whether operator.() is visible or
>not, I tend to think that this is the simplest direction: operator.()
>always applies where an actual dot occurs in the source code, for a
>class that defines the operator, and never applies otherwise.
>
>However, I would add two constraits:
>
>(1) any class which contains an operator.() can't have any usable public
>    named (non-operator) members, so they might as well be deemed to
>    make the program ill-formed.  (This could be relaxed later, but I
>    think this way would minimise the possibility of silent changes if
>    operator.() is added to or removed from a class.
>
A public member could still be accessed via the built-in -> though,
or, if operator-> is also overloaded, virtual members could be
accessed via a cast to a base class without an overloaded operator.
(which would be a useful idiom for accessing these hidden members).
>...
>(2) make operator.() non-inheritable, analogously to operator=().  This
>    means that a derived class wouldn't suddenly change its meaning
>    because one of its base classes has an operator.() added or removed.
This issue would already arise with operator->, so I think operator.
should be the same as operator-> in this regard. I don't think there
is currently any restriction in the draft on operator->'s  being
virtual (although I can't conceive of any circumstance where I would
want it make it virtual).

,Brian Parker (bparker@gil.com.au)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: bparker@gil.com.au (Brian Parker)
Date: 1997/05/26
Raw View
fjh@mundook.cs.mu.OZ.AU (Fergus Henderson) wrote:
>...
>   If you do want to access members of `DiskReference', then you need
>to do so without using the dot operator.  If the member(s) you want to
>access are overloaded operators, then you can just use the operator
>notation. For other members, if you haven't overloaded `operator&()',
>you can use `(&x)->m'.  But it's often nice to overload operator& to
>return a smart pointer.  In this case, the best thing to do is to put
>most of the details of DiskReference in a base class, say
>DiskReferenceControl, which doesn't overload operator dot, and then
>invoke the members using a reference to DiskReferenceControl. For
>example:
>      foo(DiskReference& x) {
>          DiskReferenceControl& x_control(x);
>          x_control.flush(); // flushes x to disk
>      }
>You could also simplify the calling of DiskReferenceControl member
>functions, using a helper function a function `control()' like this:
>      inline DiskReferenceControl& control(DiskReference& x) { return x; }
>
>      foo(DiskReference& x) {
>          control(x).flush();
>      }
>--
>Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit

This seems like an ideal solution to me.
I have often lamented the lack of an overloadable operator.( ).  In
D&E, Stroustrup discusses several complicating issues regarding it-

(1) He suggests that it should be invoked not only for explicit uses,
but also implicitly for any use of the object, e.g. in c = a + b;
where a and b are smart reference classes.
I disagree with this, as it assumes that smart reference classes
should act like built-in references in that they should have value
semantics after they are bound to their referent. I think that the
most common use of operator.( ) would be to implement proxies with
reference semantics (a' la Java) in which case one would definitely
*not* want operator.( ) implicitly invoked in such situations.

(2) If both operator.( ) and operator->( ) are overloaded then there
would be no way to access members of the reference class. But as
pointed out above, where this is necessary, simple work-arounds are
available.

Having an overloadable operator.( ) that acted analogously to
operator->( ) except that it returned a reference would be extremely
useful (for example, one could define a garbage-collected reference
class which would allow a Java-like coding style).

Given that such a definition would only require a few lines to add to
the draft standard (as it is almost identical to operator->( ) ), I
wonder if it really is too late to add it to the draft.

What were the pivotal arguments that caused the proposal to be
rejected in the first place?

,Brian Parker (bparker@gil.com.au)


---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: herbs@cntc.com (Herb Sutter)
Date: 1997/05/26
Raw View
bparker@gil.com.au (Brian Parker) wrote:
>Given that such a definition would only require a few lines to add to
>the draft standard (as it is almost identical to operator->( ) ), I
>wonder if it really is too late to add it to the draft.

Yes. :-)

...and that's even if it were really that easy.  Remember that this has been
discussed for years, and is a nontrivial design issue.  Others have argued for
different operator. semantics than you propose.

---
Herb Sutter (mailto:herbs@cntc.com)

Current Network Technologies Corp. (http://www.cntc.com)
2695 North Sheridan Way, Suite 150, Mississauga ON Canada   L5K 2N6
Tel 416-805-9088   Fax 905-822-3824
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: bparker@gil.com.au (Brian Parker)
Date: 1997/05/28
Raw View
herbs@cntc.com (Herb Sutter) wrote:

>bparker@gil.com.au (Brian Parker) wrote:
>>Given that such a definition would only require a few lines to add to
>>the draft standard (as it is almost identical to operator->( ) ), I
>>wonder if it really is too late to add it to the draft.

>Yes. :-)

>...and that's even if it were really that easy.  Remember that this has been
>discussed for years, and is a nontrivial design issue.  Others have argued for
>different operator. semantics than you propose.

>---
>Herb Sutter (mailto:herbs@cntc.com)

There is no doubt, though, that making operator.( ) analogous to
operator->( ) would be the simplest definition. Given that a
definition with more complex semantics couldn't be agreed upon, I
would have preferred to see at least the above semantics rather than
nothing, though I suppose that this leaves open the option of defining
operator.( ) in the next standard revision after more experience has
been obtained.

At least one use of the above definition is to define Java-like
garbage-collected reference classes, though admittedly this is only a
minor syntactic issue because one can, of course, just use
operator->().

,Brian Parker (bparker@gil.com.au)

---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Paul D. DeRocco" <pderocco@strip_these_words.ix.netcom.com>
Date: 1997/05/23
Raw View
Joe Buck wrote:
>
> It would be possible to do the whole thing with a template that delegates
> all of the member function calls to the "letter class", except that the
> committee rejected overloaded operator dot (Stepanov, the STL designer,
> has also written that this rejection was a mistake that makes STL less
> useful).

I've always assumed the reason you weren't allowed to redefine the dot
operator was that you need _some_ way to refer to the members. Once
you've redefined it, you lose the only way to refer to the members. Or
am I missing something?

--

Ciao,
Paul

(Please remove the "strip_these_words." prefix from the return
address, which has been altered to foil junk mail senders.)
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1997/05/24
Raw View
"Paul D. DeRocco" <pderocco@strip_these_words.ix.netcom.com> writes:

>Joe Buck wrote:
>>
>> It would be possible to do the whole thing with a template that delegates
>> all of the member function calls to the "letter class", except that the
>> committee rejected overloaded operator dot (Stepanov, the STL designer,
>> has also written that this rejection was a mistake that makes STL less
>> useful).
>
>I've always assumed the reason you weren't allowed to redefine the dot
>operator was that you need _some_ way to refer to the members. Once
>you've redefined it, you lose the only way to refer to the members. Or
>am I missing something?

Way back when this was being considered, I implemented it in GNU C++.
The following is the documentation that I included in my patch to g++ 2.3.3.
It explains how to avoid the problem you have raised.

Overloading Operator Dot for Smart References
=============================================

   Just as standard C++ allows you to overload `operator->()' to create
"Smart Pointer" classes, Gnu C++ allows you to overload `operator.()'
to create "Smart Reference" classes.

   Overloading `operator->()' or `operator.()' is different to
overloading the other binary operators, because the right hand operand
is a field name, not an expression. The overloaded operator function
takes only a single argument, the left hand operand, and returns a
pointer (for `operator->()') or a reference (for `operator.()') which
the compiler then uses in combination with the field name to produce
the final result.  Essentially, overloading operator dot is exactly the
same as overloading operator arrow except that where operator arrow
uses pointers, operator dot uses references.

   For any class X which overloads the dot operator, the compiler
replaces all expressions of the form `x.m' with `(x.operator.()).m'.
If the result of `X::operator.()' is a class type which also overloads
dot, then the result is recursively subject to the same expansion.
(Note that the first dot in `x.operator.()' is meant to represent the
builtin operator dot and is not subject to recursive expansion, for
otherwise we would get an infinite regress.)

   The overloaded `operator.()' will be invoked only when there is an
actual dot operator present in the source code. If a member function is
invoked without using a dot operator, then `operator.()' will *not* be
invoked. Conversely, a dot expression *always* invokes the appropriate
`operator.()' if that operator has been overloaded.  This means that if
class `X' contains a member function `f()' but `X' overloads operator
dot to return `Y&', then the following code
      X x;
      x.f();

calls `Y::f()', not `X::f()'.

   Most of the time, this behaviour is exactly what you want.
Sometimes however, you do want to access member functions of your smart
reference class. For example, suppose you have defined a class
`DiskReference' which acts as a reference to data which is stored on
disk. The class caches the disk value in memory, and the cache is
written back to the disk by the destructor. Your class users may
occaisionally want to explicitly flush the cache, so the `DiskReference'
class should provide a class method `flush()' to do that.

   If you do want to access members of `DiskReference', then you need
to do so without using the dot operator.  If the member(s) you want to
access are overloaded operators, then you can just use the operator
notation. For other members, if you haven't overloaded `operator&()',
you can use `(&x)->m'.  But it's often nice to overload operator& to
return a smart pointer.  In this case, the best thing to do is to put
most of the details of DiskReference in a base class, say
DiskReferenceControl, which doesn't overload operator dot, and then
invoke the members using a reference to DiskReferenceControl. For
example:
      foo(DiskReference& x) {
          DiskReferenceControl& x_control(x);
          x_control.flush(); // flushes x to disk
      }

You could also simplify the calling of DiskReferenceControl member
functions, using a helper function a function `control()' like this:
      inline DiskReferenceControl& control(DiskReference& x) { return x; }

      foo(DiskReference& x) {
          control(x).flush();
      }

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]