🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.std.c++
Article #3128

Re: Give operator. a chance

#3128
From: Fergus Henderson
Date: Sun, 28 Jan 1996 23:58
120 lines
6727 bytes
jodle@bix.com (jodle) writes:

>I'm afraid there is something I don't understand about this approach.  For
>a nontrivial class, one in which you have a delegate pointer and other
>members, how would you propose the compiler discriminate between
>overloaded (delegative) member selection and the selection of actual
>members of the class?  This would become an issue as soon as a member
>function tries to access member data or a user of the class attempts to
>form a call to a member function.

Jim Adcock proposed that if `.' was overloaded, then `.' always meant
the overloaded version, not the built-in version.  That's what I
implemented.  I think these simple semantics are quite sufficient to
achieve the desired functionality.  Here's the documentation included
in the gcc patch I previously posted, which explains how the problem
you refer to can be solved.

-----------------------------------------------------------------------------

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();
     }

-----------------------------------------------------------------------------

Another alternative not mentioned in the documentation is do just
put all the relevant functions in the smart pointer, not the
smart reference; if you need to invoke one of them on a smart
reference, use the overloaded & operator to convert from a smart
reference to a smart pointer: `(&x).foo();'.

>The -> operator has the advantage that it signals the delegation property
>of the member selection when it's performed.  It's easy enough for the
>programmer to ignore that aspect when it's adventageous for him to do so. 
>It is also easy enough to identify that aspect when it is an issue.  "I
>know this identifier is a reference or an object but here is this pointer
>member selection operator being used so I know delegation is being used."

Overloadable operator -> is great, but it only gives you smart pointers,
not smart references.  I think the approach outlined above has similar
advantages of making it clear when delegated members are being selected
and when the non-delegated members are being selected.  But the point of
overloading . rather than -> it gives you smart references, not smart pointers.


--
Fergus Henderson             	WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au              	PGP: finger fjh@128.250.37.3
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]
Technical Details
Message-ID: <9601281318.9776@mulga.cs.mu.OZ.AU>
Path: arc1-ca.pugleaf.net!cs.mu.OZ.AU!bounce-back
References: <3102AD11.1663@et.se> <4e8g4t$aa3@hermes.synopsys.com> <4easfd$puc@news2.delphi.com>

Show Full Headers
Complete Article Headers
Newsgroups: comp.std.c++
Path: arc1-ca.pugleaf.net!cs.mu.OZ.AU!bounce-back
From: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Subject: Re: Give operator. a chance
Message-ID: <9601281318.9776@mulga.cs.mu.OZ.AU>
Originator: fjh@munta.cs.mu.OZ.AU
Sender: news@cs.mu.OZ.AU (CS-Usenet)
Organization: Computer Science, University of Melbourne, Australia
References: <3102AD11.1663@et.se> <4e8g4t$aa3@hermes.synopsys.com> <4easfd$puc@news2.delphi.com>
X-Original-Date: Mon, 29 Jan 1996 00: 18:14 +1100
Date: Sun, 28 Jan 1996 23:58:45 GMT
Approved: fjh@cs.mu.oz.au
X-Auth: PGPMoose V1.1 PGP comp.std.c++
 iQBFAgUBMQwObuEDnX0m9pzZAQFk9QF8Cf/vu/js/KtVsANEP/lg2g/40bF8hNwO
 B/mrmEb51NBiYn9HHZVwGbj3Cvy1ugk6
 =5ilC