Topic: Give operator. a chance


Author: kanze@gabi.gabi-soft.fr (J. Kanze)
Date: 1996/02/09
Raw View
In article <311535F1.562A@et.se> Dan Holmsand <dan@et.se> writes:

> > Dan Holmsand <dan@et.se> writes:
> > >Is operator.() banned from the standards discussion?
>
> I'm really sorry for reopening this old wound, but many thanks anyway
> for all the answers.
>
> My reason for asking was my own "discovery" that the STL containers and
> algorithms are clearly overrated - sure, they are very efficient indeed
> for simple objects with trivial constructors. However, if you consider
> a vector<vector<string> >, and insert a new element at the beginning of
> the outer vector, the resulting code is clearly less than efficient
> (copy constructing all the strings of all the vectors is everything
> but efficient).
>
> That's why I was hoping for smart references to come to the rescue when
> needed, say by redefining the original vector as
> vector<smart_ref<vector<string> > >, to gain the speed benefit of a
> faster copy constructor, while not having to rewrite all the code
> involved.

If this turns out to be important, there is nothing in the standard to
prevent the vector class from implementing copy and assignment using
copy on write, exactly as will probably happen in most implementations
of string (where copy and assignment *are* expected to be frequent
operations).

I would suggest, however, that in the above case, you are simply not
using the appropriate container.  Perhaps what you need is a list<
vector< string > >.  The whole purpose of having different types of
containers is that each represents a distinct tradeoff.
--
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
              -- A la recherche d'une activiti dans une region francophone
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  Moderation policy:
  http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]





Author: James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de>
Date: 1996/02/03
Raw View
In article <4eqr6b$778@s3.iway.fr> Valentin Bonnard
<bonnardv@pratique.fr> writes:

|> First of all, I suppose there is no difference bewten A + B,
|> A.operator+ (B) and A.plus (B) when operator+ is defined as plus.

|> Suppose first you overload operator+, then you overload operator. in
|> MyClass:

|> class MyClass {
|> private:
|>    AnnotherClass* theOtherClass;
|> public:
|>    AnnotherClass operator. ();
|>    MyClass       operator+ (int);
|>    void          Something_only_defined_in_MyClass ();
|> };

|> MyClass A;

|> A + 2; is equivalent with A.operator (2) and (A.operator.).operator (2)

No, at least not as I understand the proposal.  A + 2 causes
MyClass::operator+ to be called, with &A as this, and 2 as the
argument.  The operator.() function would only be called when a `.'
appears in the equation.

|> You can't even write (&A)->Something_only_defined_in_MyClass () in
|> order to access Something_only_defined_in_MyClass (which is only
|> defined for MyClass): (&A)->a_member is equivalent with
|> ((A.operator.).operator&)->a_member.

No, the type of `&A' is MyClass*.  There is no operator.() defined for
this type, nor can there ever be.  There is an operator-> defined, the
built in one.

If I write (&A)->, the compiler uses (the built-in) operator&, then
operator-> on the results.  What makes you think it could do anything
else?

|> How can yu access Something_only_defined_in_MyClass ?

|> If operator. return theOtherClass, why don't you just write:
|> AnnotherClass* theOtherClass;

Because typically, `AnotherClass' is a proxy class (a smart
reference), the user doesn't even know about it, and cannot name it if
he is (since it will be declared private in the container class).

--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
                -- A la recherche d'une activiti dans une region francophone
---
[ 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. ]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/02/03
Raw View
Valentin Bonnard <bonnardv@pratique.fr> writes:

>First of all, I suppose there is no difference bewten A + B, A.operator+ (B)
>and A.plus (B) when operator+ is defined as plus.

No, not in the semantics which Jim Adcock proposed and which I implemented.
The rule is very simple: you invoke the `operator . ()' function, if any,
if and only if the source code contains a `.'.  So `A.operator+(B)' and
`A.plus(B)' will invoke `operator . ()', but `A + B' will not.

>Suppose first you overload operator+, then you overload operator. in MyClass:
[...]
>MyClass A;
>
>A + 2; is equivalent with A.operator (2) and (A.operator.).operator (2)

Nope, according to Jim Adcock's proposal, `A + 2' would not invoke the
user-defined `operator . ()' function.  See above.

>You can't even write (&A)->Something_only_defined_in_MyClass () in order to
>access Something_only_defined_in_MyClass (which is only defined for MyClass):

Nope, according to Jim Adcock's proposal, for your example you are free to
do so.

>Note that A.m _is equivalent_ (provided that operator. is not overloaded in
>AnnotherClass) with (&A)->m.

Nope, according to Jim Adcock's proposal that is not necessarily true either.
This is just like `x++' not necessarily being equivalent to `x = x + 1'
if you have overloaded `operator ++'.

With Jim Adcock's proposal, if you overload `operator .', you can make
the equivalence hold, by also overloading `operator &' to return a
smart pointer class.  Without his proposal, if you overload `operator &',
in the general case there is no way to make the equivalence hold.

--
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. ]





Author: John Max Skaller <maxtal@suphys.physics.su.oz.au>
Date: 1996/02/04
Raw View
Joe Buck wrote:
>
> Dan Holmsand <dan@et.se> writes:
> >Is operator.() banned from the standards discussion?
>
> Yes, I suppose it is, but since you brought it up I'll discuss it anyway.
>
> A formal proposal to add it (from Jim Adcock of Microsoft) was voted down
> some time ago.  []
>
> Some committee members objected that it's against the philosophy of C++ to
> change the meaning of a builtin operator.[]
>
> Others favored a more complex and non-orthogonal version, or, worse, first
> said the proposal should be more complex and non-orthogonal and then
> objected to the complexity and non-orthogonality in this modified version.
>
> Others objected that if both operator. and operator-> were overloaded the
> class would be just about impossible to implement, []
>
> Others (those I respect the most) just said they didn't want extensions
> without an extremely strong reason, didn't see a strong enough reason for
> operator dot, but just wanted to get C++ standardized.  []

 For the record the _principal_ argument against
operator.() which killed the proposal was simply that it
failed to actually provide smart references: an explicit
dot was required.

 For pointers, operator-> is OK, because
when no -> is used, you want a pointer and get
a smart pointer, and when you want an lvalue,
you have to use and explicit -> or * anyhow.

 But for references, this is not so:
"f(lvalue &)" requires an lvalue argument.
Operator.() (as proposed by Jim Adcock) doesn't
convert the smart reference object to a refernece
to its delegee in this context.

 As I understand it this fact was considered to
cast enough doubt on the utility of Jim's proposal
to reject it, particularly considering the potential
confusion created, and the general lack of consensus
on the issue.


--
John Max Skaller               voice: 61-2-566-2189
81 Glebe Point Rd              fax:   61-2-660-0850
GLEBE NSW 2037                 web: http://www.maxtal.com.au/~skaller/
AUSTRALIA                      email: skaller@maxtal.com.au
---
[ 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. ]





Author: Dan Holmsand <dan@et.se>
Date: 1996/02/05
Raw View
> Dan Holmsand <dan@et.se> writes:
> >Is operator.() banned from the standards discussion?

I'm really sorry for reopening this old wound, but many thanks anyway
for all the answers.

My reason for asking was my own "discovery" that the STL containers and
algorithms are clearly overrated - sure, they are very efficient indeed
for simple objects with trivial constructors. However, if you consider
a vector<vector<string> >, and insert a new element at the beginning of
the outer vector, the resulting code is clearly less than efficient
(copy constructing all the strings of all the vectors is everything
but efficient).

That's why I was hoping for smart references to come to the rescue when
needed, say by redefining the original vector as
vector<smart_ref<vector<string> > >, to gain the speed benefit of a
faster copy constructor, while not having to rewrite all the code
involved.

#pragma dream on

Another way out would be the ability (and now I am dreaming again, I
know) to tell the standard algorithms how to move an object from one
location in memory to another (as opposed to copying them), say by
means of a couple of simple functions - "move" and "uninitialized_move"
(corresponding to the copy functions), whose default implementation
would be to copy construct an object in a new place, and destroy its
copy in the old location. That function could easily and safely be
specialized for, say, vector<T, Allocator> and string, to be a simple
copying of pointers. I guess that nearly all copy constructors could be
optimized in this way, given that the old copy is guaranteed not to be
destroyed nor used in the future.

In effect, a standardized "move" function would give the language the
"move constructor" that the auto_ptr seems to be needing.

That should make, for example, the reshuffling of vectors and sorting
of containers efficient for "move aware" objects with nontrivial
constructors.

#pragma dream off

However, the real solution for me will be to ALWAYS use "pointers" in
collections, since I can always, easily, and without rewriting
everything change from one smart pointer (say, my current favourite,
the fabulous gc_ptr<T> :-) to another (containing, say, a real T if the
profiler says I can live with the copying overhead).

The fact that I can do this, without losing any efficiency and without
making any hacks in the standard algorithms, has a great deal to say in
favour of the proposed standard.

Thanks again,

Dan Holmsand
Affdrsvdrlden
dan@et.se
---
[ 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. ]





Author: John Max Skaller <maxtal@suphys.physics.su.oz.au>
Date: 1996/02/05
Raw View
Jason Merrill wrote:
>
> >>>>> Joe Buck <jbuck@Synopsys.COM> writes:
>
> > Stroustrup proposes that operator. only forward to the resulting class
> > for a.m if the object itself doesn't have a member m.

 He did?

 Stroustrup actually proposed -- AND implemented and tested:

 struct X : *Y { .. };

which looked good to me. He said that programmers found it
hard to use, however, and dropped it.

> I think a more natural way to achieve the delegation semantics proposed by
> Stroustrup would be to extend 'using' along these lines:
>
> struct A {
>   void f  ();
> };
>
> struct ARef {
>   A* a;
>   using *a;
> };

 I believe _I_ first proposed that. Again,
it looks good on the surface but until precise details
are given it isn't easy to see exactly what the
consequences will be. As with deriving from
an expression (Bjarne's idea) it may just turn out
to be to hard to use -- even if it works.
--
John Max Skaller               voice: 61-2-566-2189
81 Glebe Point Rd              fax:   61-2-660-0850
GLEBE NSW 2037                 web: http://www.maxtal.com.au/~skaller/
AUSTRALIA                      email: skaller@maxtal.com.au
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy is
  in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]





Author: claus@faerber.muc.de (Claus A. Faerber)
Date: 1996/02/05
Raw View
IMO there's a better way than 'operator.()':

Simply say: 'If a class member identifier is not found
within an object, all possible cast operators are scanned in
order to find a conversion to an object of a class
containing a member with this identifier.'

Eg.:

class A{
public: void A();
        void C();
}

class B{
public:
        void B();
        void C();
        void D();
}

class C{
        A* pA;
        B* pB;
public:
        void D();

        A& operator A&() { return *pA; };
        B& operator B&() { return *pB; };
}

//

C c;

c.A();  // calls ((A&)c).A();
c.B();  // calls ((B&)c).B();

c.C();  // calls ((A&)c).C();, as operator A& is first

c.D();  // calls c.D();, as D() is already member of C

Claus

------------------------------------------------------------------------
Claus Andre Faerber - claus@faerber.muc.de - http://www.muc.de/~cfaerber
------------------------------------------------------------------------
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy is
  in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]





Author: ark@research.att.com (Andrew Koenig)
Date: 1996/02/06
Raw View
In article <62HLdPo3cDB@faerber.muc.de> claus@faerber.muc.de (Claus A.
Faerber) writes:

> IMO there's a better way than 'operator.()':

> Simply say: 'If a class member identifier is not found
> within an object, all possible cast operators are scanned in
> order to find a conversion to an object of a class
> containing a member with this identifier.'

I suspect that makes compilation impossibly hard in the presence of templates.
--
    --Andrew Koenig
      ark@research.att.com
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  Moderation policy:
  http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]





Author: BAFlorma@softart.com (BAFlorma)
Date: 1996/02/06
Raw View
I'm not sure that this is appropriate for comp.std.c++, but I suppose that's
what moderation is for.  Maybe this is an issue for the subsequent standard
(C++ 2007?).

[Moderator's note: we do accept articles that deal with C++ language
design issues, even if they discuss a possible change to the language
that's unlikely to make it into the standard.  Consider, for example,
the recent operator. discussion.  Note also that questions about why
the language was designed one way, instead of another, are usually
legitimate topics for this group.  mha]

I've never been particularly happy with the -> operator in C, much less C++.
It strikes me as being quite redundant.  In C, an expression with the form
p.m has no legitimate meaning if p is an instance of a pointer type.  To
access the m member of the object addressed by p one needs to write p->m.
In C this was just a little ugliness, but in C++, where one may legitimately
wish to alter the access mechanism for an object from ponters to references
or vice versa over the lifetime of a project, it's a downright nuisance.  If
the compiler knows that p is a pointer type and therefore has no m member
itself, why can't it automatically dereference the pointer in the same manner
that the built in operator() automatically dereferences a pointer to function?

Obviously this would significantly blur the distinction between pointers and
references.  Perhaps this would have been a good thing.  Do we really need
pointers and references in the same language?  Wouldn't the following rules,
had they been added to C++ around 1983, have been sufficient to implement the
basic functionality of references without adding the new concept?

1. Whenever the expression p.m appears and p has no member named m, the
   expression is replaced by (*p).m if and only if there is an operator*
   applicable to the type of p.

2. Whenever the expression f(x) appears and there exists no overload of
   f which takes an argument matching the type of x, the expression is
   replaced by either f(*x) or f(&x) depending on which yields a valid
   expression.  If both do, the expression is ambiguous.

Rule 2 would extend to overloaded operators as well, so that the b - c case
that Dr. Stroustrup mentions in D&E 3.7 would implicitly be replaced by
operator-(&b, &c) when b and c are large objects.

With these rules in place, could not delegation be implemented transparently
via a "smart pointer" class and without any need to overload operator. (or
operator-> for that matter)?

Perhaps I've missed some crucial aspect of references.  I haven't devoted a
great deal of contemplation to this, but the only feature of references not
replicated by the above that I can see right offhand is that pointers lack
the prohibition against rebinding.  Obviously there would be occations when
coding in a language implemented with the above rules, especially when
applying operators, that pointers would not be as transparent as the current
C++ references are.  Nevertheless, I wonder if this might not have been a
cleaner approach overall.

I suppose this would have been a much better topic for discussion a decade
ago, but I was still a lisp hacker back then and had barely heard of C++.
:-)

--Bruce

-----------------------------------------------------------------------------
Bruce A. Florman    \ Software Engineer specializing in OO Languages and C++.
bflorman@indy.net    \ Former Lisp hacker.  Former skydiver.  Ex-Virginian.
BAFlorman@softart.com \ Fan of Joe Gibbs Racing and the Washington Redskins.
-----------------------------------------------------------------------------
 "Tragically, this serene metropolis lies directly beneath the Hoover Dam."
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy is
  in http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]





Author: claus@faerber.muc.de (Claus A. Faerber)
Date: 1996/02/19
Raw View
Jerry Coffin <jcoffin@rmii.com> (08 Feb 96):

> claus@faerber.muc.de (Claus A. Faerber) wrote:
>
> >IMO there's a better way than 'operator.()':
>
> >Simply say: 'If a class member identifier is not found
> >within an object, all possible cast operators are scanned in
the order of declaration
> >order to find a conversion to an object of a class
> >containing a member with this identifier.'
>
> Which would be chosen if two or more possibilities would work?

In the order in which the cast operators appead in the class
definition.

And cast-to classes should be scanned in the same manner as
the original class in order to find the member, ie. if one
class the object is casted to doesn't contain the member,
but a cast op. to a class, which contains it, this path is
choosen instead of the next cast of the original class.


Claus

------------------------------------------------------------------------
Claus Andre Faerber - claus@faerber.muc.de - http://www.muc.de/~cfaerber
------------------------------------------------------------------------
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  Moderation policy:
  http://reality.sgi.com/employees/austern_mti/std-c++/policy.html. ]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/01/26
Raw View
I agree with Joe Buck - this issue is at this stage dead and buried, at
least for this round of standardization.  But I can't let comments like
these pass unchallenged ;-)

David Byrden <100101.2547@compuserve.com> writes:

>New facilities are not added to the C++ language when their effect can be
>attained by a little programming using existing facilities.
>
>From your description, it seems to me that an your operator.() object
>method would return a reference to another object, and the named member
>function would be called in that object. For example, here is class
>Handle acting as a proxy for a class Bitmap;
[...]
>Operator.() was not overloaded because you can achieve exactly this same
>effect without it,

Perhaps you can in this particular case...

>if the clas Handle has a set of member functions matching those in Bitmap,

... but only with a lot of tedious programming.  Furthermore, all those
forwarding functions are a maintenance problem; every time you add
a new function to Bitmap, you need to remember to add one to Handle.

Now, please tell me how to write a template smart reference class without
using operator.()?  Can I attain that effect "with a little programming
using existing facilities"?  I think not.

--
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. ]





Author: austern (Matt Austern)
Date: 1996/01/26
Raw View
In article <4easfd$puc@news2.delphi.com> 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.

There's a long discussion of operator. in D&E, and some of it
addresses precisely this point.  If you're interested in operator.,
you may find that discussion useful: it may not change your opinion of
whether or not operator. is a good idea, but it'll probably give you a
clearer idea of some of the issues.

I've never had a strong opinion about operator. one way or the other
(I have slightly different operator fetishes), but I do think it's
quite clear that operator. will not make it into the C++ standard.
Still, it's interesting from a theoretical and historical perspective.

--
Matt Austern
SGI: MTI Compilers Group
austern@isolde.mti.sgi.com
---
[ 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. ]





Author: jodle@bix.com (jodle)
Date: 1996/01/26
Raw View
Joe Buck (jbuck@Synopsys.COM) wrote:
: Dan Holmsand <dan@et.se> writes:
: >Is operator.() banned from the standards discussion?

: Yes, I suppose it is, but since you brought it up I'll discuss it anyway.

[text elided]

: I think that this rejection was a mistake, as the lack of operator dot
: complicates the implementation of delegation (e.g. in classes that use a
: reference-counted representation object where the representation object
: has virtual functions).  I subscribe to the "write things once"
: philosophy, and hate long lists like

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.

Only one scheme is immediately apparent to me; use a two-step member
selection candidate approach.  Whether actual or delegative members are
given precedence in such a scheme, the approach would be incongruent and
perhaps contradictory with how the language solves other problems.

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."
---
[ 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. ]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/01/26
Raw View
jbuck@Synopsys.COM (Joe Buck) writes:

>In the meantime, anyone who wants to experiment with the idea can probably
>track down g++ patches that implement operator dot along the lines of
>the Adcock proposal -- I've seen mention of these on the net, but I
>don't know where they are.

I originally made the patches by modifying gcc-2.3.3.
The patches, updated to apply to gcc-2.4.0, are now available from

 http://www.cs.mu.oz.au/~fjh/comp.std.c++/gcc-2.4.0-opdot.patch

I think the 2.4.0 patches applied cleanly to 2.6.3, but I haven't tried
them with 2.7.2.  The patches include additions to the gcc documentation
which describe the exact semantics implemented.

--
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. ]






Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/01/28
Raw View
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. ]





Author: Dick Menninger <Dick.Menninger@daytonoh.attgis.com>
Date: 1996/01/30
Raw View
> ==========Fergus Henderson, 1/26/96==========

[stuff deleted]

> David Byrden <100101.2547@compuserve.com> writes:

 [...]

> >Operator.() was not overloaded because you can achieve exactly
> this same
> >effect without it,

> Perhaps you can in this particular case...

> >if the clas Handle has a set of member functions matching those
> in Bitmap,

> .... but only with a lot of tedious programming.  Furthermore,
all those
> forwarding functions are a maintenance problem; every time you add
> a new function to Bitmap, you need to remember to add one to Handle.

> Now, please tell me how to write a template smart reference
class without
> using operator.()?  Can I attain that effect "with a little programming
> using existing facilities"?  I think not.

Now that this discussion has gone far enough to convince
me that there is real merit to this, and that extensively
discussed never implies that it is wisely decided, I have
some pragmatic questions.

Does the draft explicitly exclude what it does not require?
In this case, that means does the draft preclude compilers
from allowing this, possibly as an option?  Or can compilers
provide more extensive capabilities, possibly with enabling
flags, than the draft specifies?  Note that doing that will be
part of looking at later extensions which will have to happen
in the lull after the standardizing storm.  I hope that it is
possible as an optional extension (that is likely to become
a defacto addition in real use).

We MUST look beyond the current standardization wall
that is putting a BIG ripple in the evolution of the language.
As a user, I certainly take that perspective and try very
hard to represent it here.

Good Day
Dick
Dick.Menninger@DaytonOH.ATTGIS.COM
---
[ 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. ]





Author: jason@cygnus.com (Jason Merrill)
Date: 1996/02/01
Raw View
>>>>> Joe Buck <jbuck@Synopsys.COM> writes:

> Stroustrup proposes that operator. only forward to the resulting class
> for a.m if the object itself doesn't have a member m.  I dislike that
> approach; it is unnecessary since (&a)->m accesses it, and since
> the class is going to present itself as a disguised reference object
> in any case, only private code is going to mess around with the object's
> own members.  In member functions, one would merely write "m".

I think a more natural way to achieve the delegation semantics proposed by
Stroustrup would be to extend 'using' along these lines:

struct A {
  void f  ();
};

struct ARef {
  A* a;
  using *a;
};

main ()
{
  ARef ar = { new A };
  ar.f (); // (*(ar.a)).f();
}

Of course, that's not likely to happen either.

Jason

[ 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. ]






Author: Valentin Bonnard <bonnardv@pratique.fr>
Date: 1996/02/01
Raw View
First of all, I suppose there is no difference bewten A + B, A.operator+ (B)
and A.plus (B) when operator+ is defined as plus.

Suppose first you overload operator+, then you overload operator. in MyClass:

class MyClass {
private:
   AnnotherClass* theOtherClass;
public:
   AnnotherClass operator. ();
   MyClass       operator+ (int);
   void          Something_only_defined_in_MyClass ();
};

MyClass A;

A + 2; is equivalent with A.operator (2) and (A.operator.).operator (2)

You can't even write (&A)->Something_only_defined_in_MyClass () in order to
access Something_only_defined_in_MyClass (which is only defined for MyClass):
(&A)->a_member is equivalent with ((A.operator.).operator&)->a_member.

How can yu access Something_only_defined_in_MyClass ?

If operator. return theOtherClass, why don't you just write:
AnnotherClass* theOtherClass;

theOtherClass + 2;

Note that A.m _is equivalent_ (provided that operator. is not overloaded in
AnnotherClass) with (&A)->m.

Valentin Bonnard
bonnardv@pratique.fr
---
[ 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. ]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/01/22
Raw View
In article 1663@et.se, Dan Holmsand <dan@et.se> writes:

>Is operator.() banned from the standards discussion?

No, it is not banned, but it has already had more chances and more
discussion than any other proposal for language extension, bar none.
(One argument goes that it isn't an extension since it merely removes
a restriction, but let's not quibble.)

Operator.() was the subject of heated debate in this forum for about three
years. It was the subject of extensive debate and several proposals in the C++
Committee over a period of about three years. After a proposal was voted down
by an overwhelming majority of the committee, the major proponent
of operator.() persuaded people that it had not received fair treatement.
Yet another proposal was submitted, debated, and voted down by an
overwhelming majority. (No other firmly rejected proposal has been accorded
so many chances.)

It's hard for me to imagine that anyone could come up with new arguments
in favor of operator.() that also answer the objections to it. If you think
you can do so, go ahead, but do yourself and everyone else a favor and
acquaint yourself with the discussion that has already occurred. Note
especially the arguments in the ARM and D&E against "operator dot".
---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ 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. ]





Author: Dan Holmsand <dan@et.se>
Date: 1996/01/22
Raw View
Is operator.() banned from the standards discussion?

Such a creature would sure be nice to have, given the STL
containers. Suppose that you have a list<string>, and lots of
code looking like

  typedef list<string> stringlist;
  /* ....... */
  for (stringlist::iterator i = l.begin(); i != l.end(); i++)
 (*i).append(".");

Then you decide that you want to put something else into the
list. If you decide not to derive from string (e.g. using a smart
pointer instead), there is no alternative to rewriting all the
functions refering to type stringlist.

The only way to retain flexibility now, as far as I can see, is
to refrain from using constructs like (*i).function(), using
smart pointer imitating wrappers and (*i)->function()
consequently instead. But this is kind of messy...

Ideally, operator.() should allow you to have all the things
inheritance allows you to have, except for storage. I.e. a class
with operator.() should behave just as its "base class", but be
allowed to override suitable functions.

Why isn't this possible? What's the catch?

Dan Holmsand
dan@et.se
---
[ 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. ]





Author: David Byrden <100101.2547@compuserve.com>
Date: 1996/01/22
Raw View
Dan;


>>  Is operator.() banned from the standards discussion?

No, but the time for public contributions to the standards discussion
ended over 6 months ago.


>> Ideally, operator.() should allow you to have all the things
>> inheritance allows you to have, except for storage. I.e. a class
>> with operator.() should behave just as its "base class", but be
>> allowed to override suitable functions.
>> Why isn't this possible? What's the catch?

New facilities are not added to the C++ language when their effect can be
attained by a little programming using existing facilities.



Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/01/25
Raw View
In article <3102AD11.1663@et.se>,
Dan Holmsand <dan@et.se> asks

dan> Is operator.() banned from the standards discussion?

and in article <DLost8.H9B@research.att.com>,
ark@research.att.com (Andrew Koenig) replies

ark> Yes.

but in article <4e0nc3$4dp@engnews1.Eng.Sun.COM>,
clamage@Eng.Sun.COM (Steve Clamage) writes:

clamage> No, it is not banned, [...]

So who's right, the editor or the chairman? ;-)

--
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. ]






Author: jbuck@Synopsys.COM (Joe Buck)
Date: 1996/01/26
Raw View
Dan Holmsand <dan@et.se> writes:
>Is operator.() banned from the standards discussion?

Yes, I suppose it is, but since you brought it up I'll discuss it anyway.

A formal proposal to add it (from Jim Adcock of Microsoft) was voted down
some time ago.  The Adcock proposal was quite simple: "operator dot" would
work just like operator-> except that where operator-> returns a pointer,
operator. would return a reference.  The language in the standard would
be almost identical except for simple substitutions of words.  The
advantage of this was that it would be absolutely clear what the
modification would mean and what the language should be.

Some committee members objected that it's against the philosophy of C++ to
change the meaning of a builtin operator ("."  operating on class object)
-- though we already have operator& whose builtin meaning can be changed.

Others favored a more complex and non-orthogonal version, or, worse, first
said the proposal should be more complex and non-orthogonal and then
objected to the complexity and non-orthogonality in this modified version.

Others objected that if both operator. and operator-> were overloaded the
class would be just about impossible to implement, ignoring the fact that
no one (who understands that providing operator-> gives your object
pointer semantics and operator. gives your object reference semantics, and
you'd want to choose only one) would do this.

Others (those I respect the most) just said they didn't want extensions
without an extremely strong reason, didn't see a strong enough reason for
operator dot, but just wanted to get C++ standardized.  This does make
sense (it would make more sense if so many *other* extensions had not been
accepted).

I think that this rejection was a mistake, as the lack of operator dot
complicates the implementation of delegation (e.g. in classes that use a
reference-counted representation object where the representation object
has virtual functions).  I subscribe to the "write things once"
philosophy, and hate long lists like

class Foo {
private:
 FooRep * rep;
public:
 ...
 void foo() { rep->foo();}
 int bar(int arg) { return rep->foo(arg);}
 ... and about a dozen more
};

With operator. this would become

class Foo {
private:
 FooRep * rep;
public:
 FooRep& operator.() { return *rep;}
};

and now you can call foo(), bar(int) etc on objects of class Foo.  It
is possible, but confusing, to use operator-> here, but then the Foo
object looks like a pointer, and this isn't the type of programming
interface that is desired in this situation.

But it's too late for major changes at this stage; what's more important
is to complete the standard.  We lost the vote, and it's time to move on.

In the meantime, anyone who wants to experiment with the idea can probably
track down g++ patches that implement operator dot along the lines of
the Adcock proposal -- I've seen mention of these on the net, but I
don't know where they are.

--
-- Joe Buck  <jbuck@synopsys.com> (not speaking for Synopsys, Inc)

Work for something because it is good,
not just because it stands a chance to succeed.    -- Vaclav Havel
---
[ 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. ]