Topic: Address of operator


Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Thu, 23 Jun 1994 08:41:06 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>[Note: copy constructors in particular cannot be actual
>functions.

This is too strong.  Most copy constructors can be actual
functions.  It is just copy-constructors for reference types
which can't.

>Why you ask? Because they have a signature:
>
> T::T(const T&);
>
>which is equivalent to
>
> T::T(T*)
>
>functionally.

I don't buy it.
You're assuming that references are implemented using pointers.
But they're not.  Both references and pointers are implemented
using addresses.

>Now how do you pass in that T*? Answer--with a
>copy constructor:
>
> PT::PT(PT*)

No, it's not a T* pointer, it's just a machine-level address.
Addresses don't have copy-constructors.

It is fair however to say that copy-constructors *for references*
aren't real functions.  That does make sense, because the
copy constructor for a reference to type T would look like

 typedef T& RT;
 RT::RT(const RT &);

but the type `const RT &' is illegal since it's a reference to a reference.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Mon, 20 Jun 1994 07:33:35 GMT
Raw View
In article <CroDFv.342@world.std.com> miket@world.std.com (Michael Trachtman) writes:
>: maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>:
>: For example, one might well wish to prohibit a certain class of objects having
>: their address taken:
>:
>:  class NoAddress :  {
>:  public:
>:   NoAddress * operator & (void) :  { return 0; }
>:  }
>
>
>Couldn't you still take the address with:
>
>     NoAddress x;
>     NoAddress *  xp;
>
>     xp =   ::& x;   /* Use global scope & operator */

Nope.  But:

      xp =   ::operator&(x);   /* Use global scope & operator */

might possibly work.  Hummm... well... maybe not.

Actually I think there's quite a few things still up in the air about the
built-in operators.  In particular, whether or not they act like normal
user-defined functions & operators (as far as the overload rules are
concerned), what their prototypes are, and whether or not they can be
named in a intutive fashion (as I have shown above).

Certainly, one would hope that they could be named in a normal fashion
so that one could do things like pass the address of `::operator+(int,int)'
into a template instantiation as a template actual parameter, but I don't
know if even that's been finally decided or not.  Anybody else happen to
know?

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -




Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: Mon, 20 Jun 1994 08:44:51 GMT
Raw View
miket@world.std.com (Michael Trachtman) writes:

>: For example, one might well wish to prohibit a certain class of objects
>: having their address taken:
>:
>:  class NoAddress :  {
>:  public:
>:   NoAddress * operator & (void) :  { return 0; }
>:  }
>
>Couldn't you still take the address with:
>
>     NoAddress x;
>     NoAddress *  xp;
>
>     xp =   ::& x;   /* Use global scope & operator */

No, that would be a syntax error.
You could possibly try

 xp = ::operator &(x);

but that's still a bit dodgy - I'm not sure whether compilers
should reject that or not.

--
Fergus Henderson - fjh@munta.cs.mu.oz.au




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 22 Jun 1994 21:27:53 GMT
Raw View
In article <CroDFv.342@world.std.com> miket@world.std.com (Michael Trachtman) writes:
>: maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>:
>: For example, one might well wish to prohibit a certain class of objects having
>: their address taken:
>:
>:  class NoAddress :  {
>:  public:
>:   NoAddress * operator & (void) :  { return 0; }
>:  }
>
>
>Couldn't you still take the address with:
>
>     NoAddress x;
>     NoAddress *  xp;
>
>     xp =   ::& x;   /* Use global scope & operator */
>

 Good question.


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 22 Jun 1994 21:39:40 GMT
Raw View
In article <rfgCroqBz.MBJ@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>In article <CroDFv.342@world.std.com> miket@world.std.com (Michael Trachtman) writes:
>
>Certainly, one would hope that they could be named in a normal fashion
>so that one could do things like pass the address of `::operator+(int,int)'
>into a template instantiation as a template actual parameter, but I don't
>know if even that's been finally decided or not.  Anybody else happen to
>know?

 As far as I know, built-in operators are not functions
and so their addresses cant be taken. It appears that models
of the built-in operators we be constructed "as if" they
were functions for overloading purposes only.

 The compiler could generate an instance of

 operator+(int,int)

which is addressable. Extension proposal from Ron?

[Note: copy constructors in particular cannot be actual
functions. Why you ask? Because they have a signature:

 T::T(const T&);

which is equivalent to

 T::T(T*)

functionally. Now how do you pass in that T*? Answer--with a
copy constructor:

 PT::PT(PT*)

and how do you pass in that PT*? Answer with a copy constructor:

 PPT:PPT(PPT*);

and how do you pass in that PPT* ? Answer -- recurse forever.
<grin>


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 22 Jun 1994 20:35:40 GMT
Raw View
In article <1994Jun9.135709.29385@bnrmtl.bnr.ca> ldang@bnr.ca (Student) writes:
>John Max Skaller (maxtal@physics.su.OZ.AU) wrote:
>
>:  To provide smart refernces or pointers properly
>: requires a language extension to explicitly support
>: user modelling of these constructions. Overloadability
>: is just not enough.
>
>
>What do you think about an extension like the following :

>class B : mutable A
>{
>  public:
>    operator A& ();  // must be defined
>};
>
>
>
>... is expanded by the compiler to
>
>class B
>{
>  public:
>    operator A& ();
>    void public_f() { A& a = (*this); a.public_f(); }
>    void public_g() { A& a = (*this); a.public_g(); }
>}

 Yes. Vaguely something like that is needed.
Exactly what is best is another issue. I know people have
looked at using

 operator A&()

for delegation/smart references. class B : mutable A { ..}
is similar to Stroustrups class B : public *A { .. }
which he found was too hard to use.


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Fri, 10 Jun 1994 06:43:27 GMT
Raw View
In article <damian.770933864@bruce.cs.monash.edu.au> damian@cs.monash.edu.au (Damian Conway) writes:
>
>Precisely. But why should a million programmers pay for that implementation
>convenience for the rest of eternity? Programming languages were made to serve
>the programmer, not vice versa.

--- begin sarcasm mode

Gosh.  You're right.  (Now why didn't we think of that? :-)

To heck with those fussy implementors.  In C++ one should be able to write
ANY USEFUL PROGRAM in no more than five tokens.  If the implementors cannot
figure how how to provide programmers with that level of functionality (this
year, and at a low price) then they obviously are just lazy and not trying
hard enough.

--- end sarcasm mode

I have a general suggestion for *all* non-implementors who feel qualified
to philosophize about the true nature of good programming languages:  Try
being an implementor for a year or two.  *Then* come back and tell us all
about how you think a good programming language should be designed.

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- domain addr: rfg@netcom.com ----------- Purveyors of Compiler Test ----
---- uucp addr: ...!uunet!netcom!rfg ------- Suites and Bullet-Proof Shoes -




Author: damian@cs.monash.edu.au (Damian Conway)
Date: Sun, 12 Jun 1994 04:30:21 GMT
Raw View
rfg@netcom.com (Ronald F. Guilmette) writes:

 >Damian Conway writes:
 >>
 >> But why should a million programmers pay for that implementation
 >> convenience for the rest of eternity? Programming languages were made
 >> to serve the programmer, not vice versa.

 >--- begin sarcasm mode

 >Gosh.  You're right.  (Now why didn't we think of that? :-)

 > To heck with those fussy implementors. In C++ one should be able to
 > write ANY USEFUL PROGRAM in no more than five tokens. If the
 > implementors cannot figure how how to provide programmers with that
 > level of functionality (this year, and at a low price) then they
 > obviously are just lazy and not trying hard enough.

 >--- end sarcasm mode

Only some of them.

 > I have a general suggestion for *all* non-implementors who feel
 > qualified to philosophize about the true nature of good programming
 > languages: Try being an implementor for a year or two. *Then* come
 > back and tell us all about how you think a good programming language
 > should be designed.

Grow up. I know all about the difficulties of language implementation, having
done my fair share of it over the years. But even if I hadn't, as a language
user (ie: The Customer) I would still be perfectly entitled to express a
reasonable point-of-view, even (heaven forfend!) if it should differ from yours.

And the proposition that languages should be designed with convenience and
usabilty as a primary concern is certainly a reasonable one. As far as I can
tell, along with security and compatibility, convenience was one of Stroustrup's
primary objectives. Why else bother automating so many things which can already
be done (albeit inconveniently) in C?

There must always be a tradeoff between the convenience of the user and the
difficulties faced by the implementor in providing that convenience. No-one
sensibly suggests otherwise. But the bottom line is always: as the average user
generally exhibits a considerably lower level of programming skill and ingenuity
than the language implementor, upon whom should the greater burden fall?

Disparaging and disqualifying the arguments of others on the basis of your own
superiority is specious.  I am more than happy to have my view changed by well-
reasoned discussion, but I (and most of the readers of this forum, I imagine)
have little respect for an argument that boils down to: "Fuck off, peasant!".

damian
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  who: Damian Conway                 email: damian@bruce.cs.monash.edu.au
where: Dept. Computer Science        phone: +61-3-565-5184
       Monash University               fax: +61-3-565-5146
       Clayton 3168                  quote: "A pessimist is never disappointed."
       AUSTRALIA




Author: miket@world.std.com (Michael Trachtman)
Date: Mon, 20 Jun 1994 02:55:05 GMT
Raw View
: maxtal@physics.su.OZ.AU (John Max Skaller) writes:
:
: For example, one might well wish to prohibit a certain class of objects having
: their address taken:
:
:  class NoAddress :  {
:  public:
:   NoAddress * operator & (void) :  { return 0; }
:  }


Couldn't you still take the address with:

     NoAddress x;
     NoAddress *  xp;

     xp =   ::& x;   /* Use global scope & operator */

Michael Trachtman




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Fri, 17 Jun 1994 20:04:30 GMT
Raw View
In article <2t25t8$bqh@vixen.cso.uiuc.edu> huber@kazoo.cs.uiuc.edu (Jay Huber) writes:
>>[...]
>> Now, for = there is no great difficulty because the ARM
>>specifies its NOT a built-in operator for classes. Its a member function.
>>Either you define it or the compiler generates it.
>>
>> struct X {};
>> X f();
>> X x;
>> f() = x; // fine, just fine in C++ but not ISO C
>>[...]
>
>I'm not sure what your point about ISO C is.  The = operator is
>usable on structures, but not with the lhs you chose.

 Exactly. In ISO C

 f() = x;

is an error. It is NOT an error in C++. Its legal.
>
>In other words, I disagree with your claim that operator = is not
>built-in for classes.  It is; it comes straight from C.
>See section A7.17 of K&R 2nd for details on assignment expressions.

 I dont care what K&R says. In C++ assignment of
structs is NOT a built-in operator -- its a generated member.
RTFARM :-)

 What you may not know is that the committee has
decided that member functions MAY be called on temporaries,
and that a function MAY return either a const or non-const
value which affects overloading and binding of
member functions but NOT ordinary arguments (Gak!);
constructor calls act like non-const values for this purpose:

 T f();
 T const g();
 f().mem(); // calls non-const mem()
 g().mem(); // calls const mem()

and so

 f().operator=(x); // assignment to temporary
 g().operator=(x); // assignment to const temporary
 f() = x; // same as above
 g() = x; // same as above

If you dont specify any assignment, the compiler WILL generate
one if possible, and it WILL have the "type"

 T& T::operator(const T&)

if that is possible, so that assignments to const structs are illegal but
assignment to non-const ones are fine -- that includes
all functions returning T by value and constructor calls.
Note that assignments to const structs are only disallowed
because of const correctness and because of the rules for what
the type of the generated member is -- it has nothing to do with
lvalueness. The lvalueness of the object of a member
function call is irrelevant.

This is an inevitable consequence of the fact that
"lvalue/non-lvalue" in ISO C and "lvalue/rvalue" in C++
have entirely different meanings. The C++ object framework
is quite distinct from that of C -- the committee works hard
to ensure upwards compatibility but in C++ an rvalue
is an object of temporary duration whereas in C its a value,
or a formula for computing a value.

Objects -- including temporaries -- have addresses.
Values or formulas do not.

One can summarise "C is value oriented, C++ is object oriented".

------------------------------------------------------------------
Had the ARM said that operator&() was a generated member
the same would apply -- you would be able to take the address
of a temporary of class type directly. But the ARM doesnt say this,
or anything else specific enough to determine exactly what happens.

If operator& is NOT generated as a member, very special
overload resolution rules will be required for it.
It appears this is what will happen unless my proposal
to ban user defined operator&() is adopted.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Wed, 8 Jun 1994 22:00:43 GMT
Raw View
In article <damian.770933864@bruce.cs.monash.edu.au> damian@cs.monash.edu.au (Damian Conway) writes:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>
>>In article <damian.770683606@bruce.cs.monash.edu.au> damian@cs.monash.edu.au (Damian Conway) writes:

 X* X::operator&() { return this; }
>
>Yes, you'd need three more overloadings for completeness. I felt the post was
>long enough as it was :-)

 Its not clear. Does the above method, named "operator&"
hide ALL four base methods? Does it inhibit compiler generation
of the other three methods?

>
>>>How are these idioms to be supported if operator&() is removed?
>
>> They could not be transparently if overloading& banned.
>>Have you actually ever done this? Has anyone actually used
>>overloaded operator&() and though their effort was successful?
>
>I have not actually had a reason to do this...yet!

 I once started to and immediately gave up in disgust.
Its not that the rules are not specified, its that I believe
it would be a very bad design decision and perversion.

>But I'm not prepared to say
>that I will never be in a situation where overloading unary & won't be an
>optimal solution to a programming problem.

 "optimal" is irrelevant. There is ONLY the issue
of referential transparency -- "What does the code LOOK like?".

>> Most restrictions on the programmer are freedoms for
>>implementors and tools.
>
>Precisely. But why should a million programmers pay for that implementation
>convenience for the rest of eternity? Programming languages were made to serve
>the programmer, not vice versa.

 Its not implementor "convenience" that
is the issue. The issue is that restricting the programmer gives
the implementor guarrantees (contracting paradigm) which allows
the implementor to serve the programmer well.

 The issue is therefore one of balance: how can the programmers
goals best be served? It is NOT only by giving the programmer freedom
that the programmers goals are best served, in fact its often exactly
the opposite and the lack of such guarrantees and great freedoms
given to C programmers is exactly why C code cannot be properly
optimised.

 For example the well known FORTRAN rule against
passing the same array to a function for two arguments allows
the compiler to generate more optimum code by assuming function
arguments cannot be aliased.

 The freedom of the C programmer to do that defeats
the optimisation -- this argument is commonly raised by numerical
people as a reason for sticking to FORTRAN over C++.
(I hope I got that right :-)

>> For operator&() there is no specification. Its not at
>>all clear whether such a function is inherited, for example.
>>(Assignment is not inherited). Its not at all clear if it can
>>be virtual. Its not even specified that operator& is generated
>>as a member function, and indeed the user may define it
>>as either a member or as a global.
>
>I think this is sophistry. The intent of the ARM (and most compilers) is that
>all but an arbitrary few operators can be overloaded. Part of the effort of
>standardisation is to express this intent formally and unambiguously.

 No. The intent in the case of operator& has turned out
to be a misconception that overloading operator& makes any sense,
or perhaps an oversight, considering that operator . and operator .*
are not overloadable.

 The ban on those was deliberate: they are fundamentals
of object oriented programming and implementation details of
the language with fixed semantics: some sort of immutable
reference point is required. Operator& belongs in that set.
Smart references CANNOT be implemented properly in C++,
and allowing operator.() to be overloadable would not help.

 To provide smart refernces or pointers properly
requires a language extension to explicitly support
user modelling of these constructions. Overloadability
is just not enough.

--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: ldang@bnr.ca (Student)
Date: Thu, 9 Jun 94 13:57:09 GMT
Raw View
John Max Skaller (maxtal@physics.su.OZ.AU) wrote:
:  The ban on those was deliberate: they are fundamentals
: of object oriented programming and implementation details of
: the language with fixed semantics: some sort of immutable
: reference point is required. Operator& belongs in that set.
: Smart references CANNOT be implemented properly in C++,
: and allowing operator.() to be overloadable would not help.

:  To provide smart refernces or pointers properly
: requires a language extension to explicitly support
: user modelling of these constructions. Overloadability
: is just not enough.


What do you think about an extension like the following :

class A
{
    void private_f();
  protected:
    void protected_f();
  public:
    void public_f();
    void public_g();
};



and...

class B : mutable A
{
  public:
    operator A& ();  // must be defined
};



... is expanded by the compiler to

class A
{
    //...
}

class B
{
  public:
    operator A& ();
    void public_f() { A& a = (*this); a.public_f(); }
    void public_g() { A& a = (*this); a.public_g(); }
}



--------
LD.




: --
:         JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
:  Maxtal Pty Ltd,      CSERVE:10236.1703
:         6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
:         NSW 2131, AUSTRALIA




Author: damian@cs.monash.edu.au (Damian Conway)
Date: Mon, 6 Jun 1994 20:17:44 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>In article <damian.770683606@bruce.cs.monash.edu.au> damian@cs.monash.edu.au (Damian Conway) writes:
>>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>>
>>For example, one might well wish to prohibit a certain class of objects having
>>their address taken:
>>
>> class NoAddress
>> {
>> public:
>>  NoAddress * operator & (void)
>>   { return 0; }
>> }

> This doesnt "prohibit" address taking, it stops you doing
>it directly with operator& -- but only for a non-const non-volatile
>lvalue (I think--its not exactly clear).

Yes, you'd need three more overloadings for completeness. I felt the post was
long enough as it was :-)

>>How are these idioms to be supported if operator&() is removed?

> They could not be transparently if overloading& banned.
>Have you actually ever done this? Has anyone actually used
>overloaded operator&() and though their effort was successful?

I have not actually had a reason to do this...yet! But I'm not prepared to say
that I will never be in a situation where overloading unary & won't be an
optimal solution to a programming problem.

>>I must confess that I am of the libertarian school when it comes to operator
>>overloading. C++ has always had the philosophy that accident should be
>>guarded against, but not malice. The (IMHO) arbitrary restrictions against
>>operators . .* :: and ?: rankle because all the arguments I've heard boil
>>down to "too risky". Banning unary & seems to fall into the same school.

> Most restrictions on the programmer are freedoms for
>implementors and tools.

Precisely. But why should a million programmers pay for that implementation
convenience for the rest of eternity? Programming languages were made to serve
the programmer, not vice versa.

>> The C++ community seems to be able to live with unary & (and operator= and
>> operator,) without constant programming disasters. Indeed, the ability to
>> change the behaviours of these operators is integral to a number of very
>> clever and useful paradigms. Why this loss of nerve so late in the piece?

> Its not a loss of nerve. There is no overloadable
>operator&() to loose. It CANT be overloaded now. The ARM "says"
>you can overload it but that is not a specification.

> As it is, the ARM does not really permit ANY operator
>overloading: how do built-in operators and user defined
>ones interact with overload resolution?

> For operator&() there is no specification. Its not at
>all clear whether such a function is inherited, for example.
>(Assignment is not inherited). Its not at all clear if it can
>be virtual. Its not even specified that operator& is generated
>as a member function, and indeed the user may define it
>as either a member or as a global.

I think this is sophistry. The intent of the ARM (and most compilers) is that
all but an arbitrary few operators can be overloaded. Part of the effort of
standardisation is to express this intent formally and unambiguously.

> Other uses which prevent finding the actual address
>of an object are all theoretically suspect: obtaining
>the real address of something is fairly fundamental,
>especially in Object Oriented programming.

For primitive classes I'd agree, but the beauty (and flaw!) of C++ is that the
same mechanisms (classes and overloading) are used for creating new programming
constructs  (eg: class string, class array<int>, etc.) _and_ for modelling parts
of the problem domain (eg: class NetworkNode, class ReactorController, etc.) The
semantics of address comparison (or whatever unary & represents in a
particular problem domain) may _require_ logical, rather than physical
address-taking.

> Not having a guarrantee that &x really is the address
>of x makes programming -- say in a template -- very difficult,
>especially since there are no smart references or
>overloadable operator.().

Round we go again! :-)

damian
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  who: Damian Conway                 email: damian@bruce.cs.monash.edu.au
where: Dept. Computer Science        phone: +61-3-565-5184
       Monash University               fax: +61-3-565-5146
       Clayton 3168                  quote: "A pessimist is never disappointed."
       AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Mon, 6 Jun 1994 16:53:20 GMT
Raw View
In article <damian.770683606@bruce.cs.monash.edu.au> damian@cs.monash.edu.au (Damian Conway) writes:
>maxtal@physics.su.OZ.AU (John Max Skaller) writes:
>
>>Here's another proposal that suggests we allow the address
>>of an rvalue to be taken, and disallow overloaded unary operator&().
>
>For example, one might well wish to prohibit a certain class of objects having
>their address taken:
>
> class NoAddress
> {
> public:
>  NoAddress * operator & (void)
>   { return 0; }
> }

 This doesnt "prohibit" address taking, it stops you doing
it directly with operator& -- but only for a non-const non-volatile
lvalue (I think--its not exactly clear).

 That is, its referential transparency, not functionality,
that is the issue here.  As you probably know, referential
transparency is a two-edged sword: like overloading sometimes
it's useful and sometimes it's a pain or even dangerous.

 NoAddress* actualAddress(NoAddress& x) { return &x; }

returns the real address of a NoAddress object, but:

 NoAddress x;
 NoAddress *px, *upx;
 px = actualAddress(x);
 upx = &x;
 assert(px==upx);

fails. Indeed:

 assert(&*px==upx);
 assert(&*px!=px);


Whether the actual address or the pretend address is needed depends
on the CLIENT code. Overloading operator&() inverts the standard idiom,
"& gets the real address, use member getMeaning() to get the
address of the refered to object" making operator& an unreliable
way to get the actual address when you really need it.

>
>Or one might be building a system where logical rather that physical ddresses
>are more useful:
>
> class LogicalAddress
> {
> public:
>  int operator & (void)
>   { return hash(data); }
>
> private:
>  Data data;
> }
>
>How are these idioms to be supported if operator&() is removed?

 They could not be transparently if overloading& banned.
Have you actually ever done this? Has anyone actually used
overloaded operator&() and though their effort was successful?
>
>I must confess that I am of the libertarian school when it comes to operator
>overloading. C++ has always had the philosophy that accident should be
>guarded against, but not malice. The (IMHO) arbitrary restrictions against
>operators . .* :: and ?: rankle because all the arguments I've heard boil down
>to "too risky". Banning unary & seems to fall into the same school.


 Most restrictions on the programmer are freedoms for
implementors and tools.

>
>The C++ community seems to be able to live with unary & (and operator= and
>operator,) without constant programming disasters. Indeed, the ability to
>change the behaviours of these operators is integral to a number of very clever
>and useful paradigms. Why this loss of nerve so late in the piece?

 Its not a loss of nerve. There is no overloadable
operator&() to loose. It CANT be overloaded now. The ARM "says"
you can overload it but that is not a specification.

 As it is, the ARM does not really permit ANY operator
overloading: how do built-in operators and user defined
ones interact with overload resolution?

 The ARM forgot to say. Several papers (Kendall, Welch for
example) deal with this problem by modelling the built-in operators
as functions so overloading (which only applies to functions) can
work without special rules.

 Now, for = there is no great difficulty because the ARM
specifies its NOT a built-in operator for classes. Its a member function.
Either you define it or the compiler generates it.

 struct X {};
 X f();
 X x;
 f() = x; // fine, just fine in C++ but not ISO C

 For operator&() there is no specification. Its not at
all clear whether such a function is inherited, for example.
(Assignment is not inherited). Its not at all clear if it can
be virtual. Its not even specified that operator& is generated
as a member function, and indeed the user may define it
as either a member or as a global.

 So: its extremely difficult to specify exactly
what the semantics of operator&() should be: what it should
do depends on usage, and the main use I can see for it is
to circumvent the restriction on addressing an rvalue.

 Other uses which prevent finding the actual address
of an object are all theoretically suspect: obtaining
the real address of something is fairly fundamental,
especially in Object Oriented programming.

 Not having a guarrantee that &x really is the address
of x makes programming -- say in a template -- very difficult,
especially since there are no smart references or
overloadable operator.().

 Therefore, banning user defined operator&() is just
one of several options: which ever one is chosen will break
code that assumes a different model because there IS NO MODEL
in the ARM.

>On the other hand, I think the ability to take the address of temporaries (now
>that their lifespan is well-defined) is an excellent proposal.

 It introduces additional risks. I think they are
counterbalanced by the advantages. Not being able to actually
do it I can't experiment to find out. However, the risk is
much smaller than allowing a nonconst reference to bind to
a temporary: that would happen silently, addressing an rvalue
requires the programmer to write an explicit "&".

 It also seems consistent: rvalues CAN be addressed anyhow
in C++,  and treating "temporary" as just another storage class
in conjunction with the notion that storage class is not part of
type and is ignored in overloading makes sense (but that would
require binding non-const references to them :-)


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA




Author: huber@kazoo.cs.uiuc.edu (Jay Huber)
Date: 7 Jun 1994 16:06:00 GMT
Raw View
>[...]
> Now, for = there is no great difficulty because the ARM
>specifies its NOT a built-in operator for classes. Its a member function.
>Either you define it or the compiler generates it.
>
> struct X {};
> X f();
> X x;
> f() = x; // fine, just fine in C++ but not ISO C
>[...]

I'm not sure what your point about ISO C is.  The = operator is
usable on structures, but not with the lhs you chose.  If you
fix a few problems, something like this works fine:

  struct X { int a; };   /* can't have zero size struct */
  struct X x;

  main() {
    struct X y;
    x = y;               /* legal */
    }

In other words, I disagree with your claim that operator = is not
built-in for classes.  It is; it comes straight from C.
See section A7.17 of K&R 2nd for details on assignment expressions.




Author: damian@cs.monash.edu.au (Damian Conway)
Date: Fri, 3 Jun 1994 22:46:46 GMT
Raw View
maxtal@physics.su.OZ.AU (John Max Skaller) writes:

>Here's another proposal that suggests we allow the address
>of an rvalue to be taken, and disallow overloaded unary operator&().

In a later post he asks for comment.

My comment is that whilst the proposal _does_ help guard against a particularly
nasty problem (namely: explicitly taking the address of a temporary), it ignores
the fact that operator overloading is not always used to perform the obvious
operation.

For example, one might well wish to prohibit a certain class of objects having
their address taken:

 class NoAddress
 {
 public:
  NoAddress * operator & (void)
   { return 0; }
 }

Or one might be building a system where logical rather that physical ddresses
are more useful:

 class LogicalAddress
 {
 public:
  int operator & (void)
   { return hash(data); }

 private:
  Data data;
 }

How are these idioms to be supported if operator&() is removed?

I must confess that I am of the libertarian school when it comes to operator
overloading. C++ has always had the philosophy that accident should be
guarded against, but not malice. The (IMHO) arbitrary restrictions against
operators . .* :: and ?: rankle because all the arguments I've heard boil down
to "too risky". Banning unary & seems to fall into the same school.

The C++ community seems to be able to live with unary & (and operator= and
operator,) without constant programming disasters. Indeed, the ability to
change the behaviours of these operators is integral to a number of very clever
and useful paradigms. Why this loss of nerve so late in the piece?

On the other hand, I think the ability to take the address of temporaries (now
that their lifespan is well-defined) is an excellent proposal.

damian
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  who: Damian Conway                 email: damian@bruce.cs.monash.edu.au
where: Dept. Computer Science        phone: +61-3-565-5184
       Monash University               fax: +61-3-565-5146
       Clayton 3168                  quote: "A pessimist is never disappointed."
       AUSTRALIA




Author: maxtal@physics.su.OZ.AU (John Max Skaller)
Date: Sat, 28 May 1994 15:18:01 GMT
Raw View
Here's another proposal that suggests we allow the address
of an rvalue to be taken, and disallow overloaded unary operator&().
-------------------------------------------------------------------------
            ISO:       WG21/N0xxx
            ANSI:      94-xxxx
            Author:    John Max Skaller
            Date:      May 1994
            Reply to:  maxtal@suphys.physics.su.oz.au



The ADDRESS-OF OPERATOR
-----------------------

Executive Summary
-----------------

This paper is contains two proposals for changing the C++
language -- an extension and a restriction. The restriction
depends on the extension.

1) Extension -- allow the address of an rvalue to be taken.

2) Restriction (optional) -- disallow overloading unary operator&.


The problem
-----------

1) It is necessary to be able to address rvalues -- in C++ class rvalues
   are objects, and as such are useless unless member functions can be called
   on them -- which requires their address be obtained to yield
   the 'this' pointer. It seems inconsistent to allow the address to be
   taken indirectly and implicitly but not explicitly: programmers
   do not favour being denied access and will code dangerous
   and non-standard workarounds.

     T const *cadr(T const& t) { return &t; }
     T *adr(T const& t) { return const_cast<T*>(&t); }
     T g();
     cadr(g()); // obtain const pointer to rvalue
     adr(g()); // obtain non-const pointer to rvalue

   This code is simple, and can be templated.
   Although the const version cadr() does not permit modification
   without a visible cast, dangling references can still occur.

   The non-const version not only permits modification, but
   will silently provide a non-const address of a const object.

2) One of the idioms for working around this problem is use of
   user defined operator&.

     class Base {
       Base * operator&(){ return this; }
     };

     class Derived : public Base {};

     Base b();
     &b(); // address of rvalue

     Derived d();
     &d();  // result not specified by ARM

   However, it is not clear how
   user defined operator&, whether global or member, interacts
   with the built-in operator&.
   Failure to provide these functions in every derived class
   has indeterminate results.

   Const correct results actually require 4 functions, which
   is best handled by a macro:

     #define addressing(T) \
       T* operator&(){ return this; } \
       T const * operator&() const { return this; } \
       T volatile * operator&() volatile { return this; }
       T const voldatile * operator&() const volatile { return this; }

     class Base {
       addressing(Base)
     };

   If you want the address of the complete object,
   you can do that too using a virtual function with a covariant
   return:

     class Base {
       virtual Base * operator&() { return this; }
     };

     class Derived {
       virtual Derived * operator&() { return this; }
     };

   However these techniques are extremely dangerous and do
   not lead to simple, readable code. Many of the standard
   laws of C are broken, and without overloadable operator.()
   cannot be restored.

3) It seems inconsistent and needlessly complex to ban explicit
   rvalue addressing when the lifetime of temporary objects is
   quite specific and well determined. For the storage class
   static deletion is dangerous, but not prevented. For the storage
   class auto, deletion and dangling pointers are dangerous,
   but are not prevented. It is the philosophy of C++ to allow
   useful operations which are none-the-less potentially dangerous,
   and this precedent is clearly established for memory allocation
   issues. Why should temporary storage class be treated differently?

     int x;
     int *px = &x;

     int g();
     void f() {
       delete px; // WOOPS
       int x;

       px = &x;
       delete px; // WOOPS

       px = *g();
       delete &g(); // WOOPS
     }


4) The inability to explicitly address rvalues conflicts directly
   with a sensible idiom for parameter passing: use a reference
   in a constructor or function if the argument does not need to persist
   beyond the life of the constructor call, use a pointer if
   the argument must persist throughout the life of the object.

   Using this idiom, pointers are indicated for handle classes,
   but it makes sense to make temporary handles. In that case,
   explicit (and thus visually obvious) address taking is required,
   and it is easy to check the argument is being passed to
   a temporary.

   For example:

     template<class T>
     class Handle {
       T* delegate;
     public:
        Handle(T* t) : delegate(t) {}
        Handle(T& t) : delegate(&t) {}
        void doit() { delegate->doit(); }
     };

     // pointer protocol prefered

     Handle<complex> h(&complex(1,2));
       // unsafe -- handle outlasts the complex number
       // explicit & visible

     void f(Handle<complex> const&);
     f(Handle<complex>(&complex(1,2));
       // safe -- complex number outlasts handle

     // reference protocol currently required

     Handle<complex> h(complex(1,2));
       // unsafe -- handle outlasts the complex number
       // error

     typedef const complex constcomplex;
     Handle<constcomplex> h(complex(1,2));
       // unsafe -- handle outlasts the complex number
       // no diagnostic

     complex z;             // pollute namespace
     Handle<complex> h(&z); // safe
     return h;              // woops!

   One hopes no programmer is tempted to write:

        Handle(T const& t) : delegate( const_cast<T*>(&t) ) {}

   to get around this problem!


Advantages of the changes
-------------------------

1) Simpler and more comprehensible: consistent treatment of storage
   classes.

2) Simpler and more comprehensible: breaches of the One Definition
   Rule caused by taking the address of an lvalue of incomplete
   type which is later determined to have a user defined address-of
   operator can no longer happen.

3) Explicit addressing of rvalues is detectable and warnings may be
   issued by quality implementations.

4) No code is broken by allowing rvalues to be addressed.

5) Disallowing overloading of operator& may break some code,
   but some of it is probably not well defined anyhow, and
   some exists only to circumvent the restriction on addressing rvalues.

6) The difficult task of deciding how user defined and built-in
   address-of operators interact is obviated if overloading is
   not permitted.

7) Non-const references may be bound to temporaries using the
   following idiom:

      T g(T&);
      T t;
      g(*&g(*&T()));

   The use of *& is explicit and resembles a cast. Binding non-const
   references to temporaries remains ill-formed, so that the
   silent hijacking of an rvalue is not possible.

8) The temptation to pervert library designs to use references
   in order to allow temporary arguments is reduced.

9) Re-establish some basic laws and symmetry between . and &.


Disadvantages of the changes
----------------------------

1) Disallowing user defined address of operator may break some code.

2) Allowing rvalues to be addressed directly permits a new source
   of error. However, most existing compilers issue errors which
   can easily be changed to warnings.


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,      CSERVE:10236.1703
        6 MacKay St ASHFIELD,     Mem: SA IT/9/22,SC22/WG21
        NSW 2131, AUSTRALIA