[comp.lang.c++] friend operator +

grimlok@hubcap.clemson.edu (Mike Percy) (11/21/90)

I've tried and tried to get a handle on this question from various
sources, but haven't been enlightened...
 
What semantic/operational and/or stylistic differences are there
between these two definitions?
 
class foo {
...
public:
   foo& operator +(foo& rhs);
}
 
and
 
class foo {
...
public:
  friend foo& operator +(foo& lhs, foo&rhs);
}
 
I must be missing something, because to me these effectively are the
same thing, but the first can operate on this.
 
 
"I don't know about your brain, but mine is really...bossy."
Mike Percy                    grimlok@hubcap.clemson.edu
ISD, Clemson University       mspercy@clemson.BITNET
(803)656-3780                 mspercy@clemson.clemson.edu

Bruce.Hoult@bbs.actrix.gen.nz (11/21/90)

In article <11759@hubcap.clemson.edu> grimlok@hubcap.clemson.edu (Mike Percy) writes:
> I've tried and tried to get a handle on this question from various
> sources, but haven't been enlightened...
>  
> What semantic/operational and/or stylistic differences are there
> between these two definitions?
>  
> class foo {
> ...
> public:
>    foo& operator +(foo& rhs);
> }
>  
> and
>  
> class foo {
> ...
> public:
>   friend foo& operator +(foo& lhs, foo&rhs);
> }
>  
> I must be missing something, because to me these effectively are the
> same thing, but the first can operate on this.


The important difference is that the friend version allows implicit
conversions to be made on both the left hand and right hand arguments
to +, wheras the member function version allows implicit conversions
on only the RH argument.

randolph@tuna.ssd.kodak.com (Gary L. Randolph) (11/21/90)

In article <11759@hubcap.clemson.edu> grimlok@hubcap.clemson.edu (Mike Percy) writes:
>I've tried and tried to get a handle on this question from various
>sources, but haven't been enlightened...
 
>What semantic/operational and/or stylistic differences are there
>between these two definitions?
 
>class foo {
>...
>public:
>   foo& operator +(foo& rhs);
>}
 
>and
 
>class foo {
>...
>public:
>  friend foo& operator +(foo& lhs, foo&rhs);
>}
> 
>I must be missing something, because to me these effectively are the
>same thing, but the first can operate on this.
                           ^^^^^^^^^^^^^^? "requires" 


The best way to see the difference is by trying to use each.

Say the member function is defined (No friend yet).  Assume for the
sake of example that a constructor, foo::foo(int), exists.  
The following:

    main(){
       foo a,b;
       a + b;    //ok
       b + a;    //of course
       a + 2;    //Fine, equivalent to a + foo(2);

       2 + a;    //Problem

Why?  If operator+ is a member, then operator+ must be called on
behalf of an object. (Pg 253 of ARM states that for dot/arrow notation,
the first expression must be an object/pointer to object.)  So, why can't
the 2 be converted to an object of type foo and then have operator+
applied to that temporary?  As you know, 

     a + 2; 

becomes   

     a.operator(2);

Pg 333 (ARM) states that, for binary operators of the form

     x.operator+(y)	

NO USER-DEFINED CONVERSIONS WILL BE APPLIED TO x.  So, for a + 2, the 
literal, 2, can be converted, but the a *must* be an object of type foo.

So, 
     2 + a;

fails.  Note that no non-foo object can be used as the left operand.

This restriction is NOT made for calls of the form

     operator+(2, a);

so user-defined conversion can be made on the non-foo 2.

So, if a non-foo is to be used as the left operand, a friend is preferred.

Note that friendship is only necessary for accessing a foo's private parts.
If accessor functions were available, then the NONmember function is still
required, but friendship is not.

Gary Randolph
Eastman Kodak Company

grimlok@hubcap.clemson.edu (Mike Percy) (11/22/90)

randolph@tuna.ssd.kodak.com (Gary L. Randolph) writes:

>In article <11759@hubcap.clemson.edu> grimlok@hubcap.clemson.edu (Mike Percy) writes:
 [ I asked about friend operator +() vs operator +() ]

>so user-defined conversion can be made on the non-foo 2.

>So, if a non-foo is to be used as the left operand, a friend is preferred.

>Note that friendship is only necessary for accessing a foo's private parts.
                                                              ^^^^^^^^^^^^^
Good one.

>If accessor functions were available, then the NONmember function is still
>required, but friendship is not.
 
So, the answer is that they are the same until a non-foo is the LHS of
the binary operator.  In my particular case, there are no conversions
allowed (both LHS, RHS must be declared to be foo's; if not the
operations are meaningless), so I could use either one, and expect the
same results (please correct me if I'm wrong).  My difficulties came
from never considering that some classes use/provide for conversions in
operations. 
 
Now, the related question:  Is one prefered over the other as a
stylistic point or for any other reason?
 
Thanks for the responses, I knew I could count on the net.

"I don't know about your brain, but mine is really...bossy."
Mike Percy                    grimlok@hubcap.clemson.edu
ISD, Clemson University       mspercy@clemson.BITNET
(803)656-3780                 mspercy@clemson.clemson.edu

ahodgson@hstbme.mit.edu (Antony Hodgson) (11/22/90)

Just a warning that the use of the expression:

	foo& operator + ( foo& f )
or
	friend foo& operator + ( foo& f1, foo& f2 )

will almost invariably lead to a run-time error.  The obvious interpretation
of the + operator is that you are creating a new object from the two arguments.
This new object will therefore require its own storage.  You can either do
this by defining a foo variable local to the + function, or by using "new"
to create a new one inside +.  If you do the former, you return a reference
to an object which is susceptible to being immediately deallocated, so any
use of the foo& outside of + will give garbage.  If you do the latter, you
must keep track of the returned reference in order to properly deallocate
it later;  if you use the returned value as an intermediate result (as in
foo c = a + b + d) you will lose the ability to deallocate the result of
b+d, so you'll have permanently trapped some memory.  Do this often enough
and you'll run out of memory.  It's much better to define the operator as

	foo operator+(foo& f)

and let the system deal with deallocating it when it goes out of scope.

Good luck,

Tony Hodgson
ahodgson@hstbme.mit.edu

fox@allegra.att.com (David Fox) (11/22/90)

In article <1990Nov21.053431.22340@actrix.co.nz> Bruce.Hoult@bbs.actrix.gen.nz writes:

   The important difference is that the friend version allows implicit
   conversions to be made on both the left hand and right hand arguments
   to +, wheras the member function version allows implicit conversions
   on only the RH argument.

Another difference is that the member version can be virtual, while
the friend version cannot.

rmartin@clear.com (Bob Martin) (11/23/90)

In article <1990Nov21.053431.22340@actrix.co.nz> Bruce.Hoult@bbs.actrix.gen.nz writes:
>In article <11759@hubcap.clemson.edu> grimlok@hubcap.clemson.edu (Mike Percy) writes:
>> I've tried and tried to get a handle on this question from various
>> sources, but haven't been enlightened...
>>  
>> What semantic/operational and/or stylistic differences are there
>> between these two definitions?
>>  
>> class foo {
>> ...
>> public:
>>    foo& operator +(foo& rhs);
>> }
>>  
>> and
>>  
>> class foo {
>> ...
>> public:
>>   friend foo& operator +(foo& lhs, foo&rhs);
>> }
>>  
>> I must be missing something, because to me these effectively are the
>> same thing, but the first can operate on this.
>
>
>The important difference is that the friend version allows implicit
>conversions to be made on both the left hand and right hand arguments
>to +, wheras the member function version allows implicit conversions
>on only the RH argument.

Let me elaborate on this a bit.  Let us say that you have created
a constructor as follows:  foo::foo(int).  This tells the compiler
that it is allowable to convert an int into a foo.

Now lets say that you use the following code:

foo fa,fb;
int ia;
...
fb=ia+fa;  // adding an int to a foo.

If you use the foo::operator+(foo&) method, then the above statement
will produce a compiler error since foo::operator+(foo&) requires that
the left hand side of the + operator must be a foo.  But if you use
the friend operator+(foo&,foo&) then the compiler knows how to convert
ia into a foo before 'adding' fa.


-- 
+-Robert C. Martin-----+:RRR:::CCC:M:::::M:| Nobody is responsible for |
| rmartin@clear.com    |:R::R:C::::M:M:M:M:| my words but me.  I want  |
| uunet!clrcom!rmartin |:RRR::C::::M::M::M:| all the credit, and all   |
+----------------------+:R::R::CCC:M:::::M:| the blame.  So there.     |

jk@cs.man.ac.uk (John Kewley ICL) (11/23/90)

In article <11759@hubcap.clemson.edu> grimlok@hubcap.clemson.edu (Mike Percy) writes:
>I've tried and tried to get a handle on this question from various
>sources, but haven't been enlightened...
> 
>What semantic/operational and/or stylistic differences are there
>between these two definitions?
> 
>class foo {
>...
>public:
>   foo& operator +(foo& rhs);
>}
> 
>and
> 
>class foo {
>...
>public:
>  friend foo& operator +(foo& lhs, foo&rhs);
>}
> 
>I must be missing something, because to me these effectively are the
>same thing, but the first can operate on this.
> 

If needed, the first definition can be a private or protected function. This
is not possible with a friend.

Implicit type coersions are not permitted on the LHS of the non-friend version.
e.g. 1 + x is not allowed for the member but would be valid for the friend,
assuming a coersion from int -> foo.
 
Note that operator= (and [], (), ->) must be defined using the first method.

The second method should be used when there is a need for the parameters to be
ordered with the object as the second parameter -
operator<< usually needs to be defined using the friend syntax when it is
overloaded with the output function.

A friend can also be used for a function to operate on two objects of
different classes. It would be declared as friend within each class.

If the function + is defined in C (or another language), the friend version
would be neccessary.

In the non-operator case, friends have a different syntax (less object-oriented)
so functions such as print can be called as print(foobar) rather than 
foobar.print().

All this being said it is often unclear whether to declare a function as a
friend or a member. Where there is no over-riding need I prefer members.
--
        J.K.
 
John M. Kewley, ICL, Wenlock Way, West Gorton, Manchester. M12 5DR
Tel:   (+44) 61 223 1301 X2138  Email: jk@r6.cs.man.ac.uk / jk@nw.stl.stc.co.uk

fuchs@it.uka.de (Harald Fuchs) (11/25/90)

jk@cs.man.ac.uk (John Kewley ICL) writes:
>In article <11759@hubcap.clemson.edu> grimlok@hubcap.clemson.edu (Mike Percy) writes:
>>I've tried and tried to get a handle on this question from various
>>sources, but haven't been enlightened...
>> 
>>What semantic/operational and/or stylistic differences are there
>>between these two definitions?
>> 
>>class foo {
>>...
>>public:
>>   foo& operator +(foo& rhs);
>>}
>> 
>>and
>> 
>>class foo {
>>...
>>public:
>>  friend foo& operator +(foo& lhs, foo&rhs);
>>}
>> 
>>I must be missing something, because to me these effectively are the
>>same thing, but the first can operate on this.

>Implicit type coersions are not permitted on the LHS of the non-friend version.
>e.g. 1 + x is not allowed for the member but would be valid for the friend,
>assuming a coersion from int -> foo.

Yes, that's the main difference between the two.

>A friend can also be used for a function to operate on two objects of
>different classes. It would be declared as friend within each class.

No. A member function of one class can be a friend of another one.

>If the function + is defined in C (or another language), the friend version
>would be neccessary.

... unless your C compiler doesn't grok a function named 'operator+' :-)

>In the non-operator case, friends have a different syntax (less object-oriented)
>so functions such as print can be called as print(foobar) rather than 
>foobar.print().

"Less object-oriented"? That's debatable. It's just another syntax,
and many object-oriented languages do use that syntax (passing 'this'
as the first argument of a member function). At least you have the
main benefits of C++ OO'ness in both versions:
- Locality of information: by reading the class definition, you see what
  (member or friend) fucntions are allowed to access the private
  members.
- No namespace pollution: there are already several operator+ versions
  hanging around, so it doesn't matter if you add another one by a friend
  declaration.

>All this being said it is often unclear whether to declare a function as a
>friend or a member. Where there is no over-riding need I prefer members.

I think it's good style to use member notation if the first argument
is changed, e.g.
  foo& foo::operator+= (const foo&)
unless you must use the friend version because the class comes from a
library, e.g
  ostream& operator<< (ostream&, const foo&)
and to use the friend version if you create a new object, e.g.
  foo operator+ (const foo&, const foo&)

Note the use of '&': operator+= returns just a reference to *this,
while operator+ creates a new object, presumably not related to its
first argument.

Not also the use of 'const': I could have said something like
  foo operator+ (foo, foo)
but if your foo is large it's more efficient to pass it by reference,
and const tells us that operator+ really doesn't want to change its
arguments as it could have done if you omit the 'const'.

I _never_ use
  foo& operator+ (const foo&, const foo&)
(returning a reference) because this can lead to dangling references.
The returned value can't refer to an object on the stack because the
stack is popped when returning from operator+. It cannot refer to a
local static variable inside operator+ because in this case something like
  foo f1 = f2 + f3 + f4;
would give the wrong result. It _could_ refer to an object on heap
(allocated via 'new'), but this might lead to a memory leak: when should
you delete this object?

The returning of a value rather than a reference might be less
efficient because it calls the copy constructor
  foo::foo (const foo&)
but a good compiler could be able to optimize it away (unless there
are side effects), and I think if the copy constructor (and the
assignment operator) are performance bottlenecks one should rewrite
the class anyway (using counted pointers or something like that).
--

Harald Fuchs <fuchs@it.uka.de> <fuchs%it.uka.de@relay.cs.net> ...
<fuchs@telematik.informatik.uni-karlsruhe.dbp.de>   *gulp*

pal@xanadu.wpd.sgi.com (Anil Pal) (11/27/90)

In article <11759@hubcap.clemson.edu>, grimlok@hubcap.clemson.edu (Mike Percy) writes:
|>  
|> What semantic/operational and/or stylistic differences are there
|> between these two definitions?
|>  
|> class foo {
|> public:
|>    foo& operator +(foo& rhs);
[... and ...]
|>   friend foo& operator +(foo& lhs, foo&rhs);
|> }

1. The friend operator can perform implicit type conversions on its
first argument.  Thus if you have a foo(int) constructor, then with the
friend operator you can do things like (3 + foovar), where with the
member function operator you would have to have (foovar + 3) to get
your operator.

2. On the other hand, the member function could be made virtual where
the friend function cannot.  In a binary operation like +, however, you
need to dispatch on both argument types in any case.

-- 
Anil A. Pal,	Silicon Graphics, Inc.
pal@sgi.com	(415)-335-7279

Reid Ellis <rae@gpu.utcs.toronto.edu> (11/27/90)

rmartin@clear.com (Bob Martin) writes:
!foo fa,fb;
!int ia;
!...
!fb=ia+fa;  // adding an int to a foo.
!
!If you use the foo::operator+(foo&) method, then the above statement
!will produce a compiler error since foo::operator+(foo&) requires that
!the left hand side of the + operator must be a foo.

Not necessarily.  What if there existed "foo::operator int()" as well
as "foo:foo(int)"?  Then the above would work fine.

[Personal opinion follows]

	I don't agree with the idea that the result of adding two
	objects of different type should return any arbitrary type, as
	defined by a global function "operator+(type1, type2)".
	Rather, I have a "gut feel" that if I add an orange to an
	apple, it is up to the apple to decide what to do with it.  I
	guess this comes from English where, for example, you add an
	element to a set [resulting in a new set] but you don't add a
	set to an element..

	Does anyone else think that

		operator+(const type1 &, const type2 &);

	should have a return type of type1 at all times?

[end opinion]

					Reid

--
Reid Ellis  176 Brookbanks Drive, Toronto ON, M3A 2T5 Canada
rae@gpu.utcs.toronto.edu      ||  rae%alias@csri.toronto.edu
CDA0610@applelink.apple.com   ||             +1 416 446 1644

glenn@huxley.huxley.bitstream.com (Glenn P. Parker) (11/28/90)

In article <FOX.90Nov22085257@papyrus.tempo.nj.att.com>,
fox@allegra.att.com (David Fox) writes:
> Another difference is that the member version can be virtual, while
> the friend version cannot.

Is this really a big difference?  Global overloading can make operator+
work very much like a virtual function.  If the friend function is declared
as:

    class A {
        // ...
        friend A operator+ (const A& left, const A& right);
    };

Then classes derived from A can use operator+ automatically, *or* they can
override it as follows:

    class B : public A {
        // ...
        friend B operator+ (const B& left, const B& right);
    };

--
Glenn P. Parker       glenn@bitstream.com       Bitstream, Inc.
                      uunet!huxley!glenn        215 First Street
                      BIX: parker               Cambridge, MA 02142-1270

fox@allegra.att.com (David Fox) (11/28/90)

glenn@huxley.huxley.bitstream.com (Glenn P. Parker) writes:

>fox@allegra.att.com (David Fox) writes:
>> Another difference is that the member version can be virtual, while
>> the friend version cannot.
> Is this really a big difference?  Global overloading can make operator+
>work very much like a virtual function.  If the friend function is declared
>as:
>     class A {
> // ...
> friend A operator+ (const A& left, const A& right);
>    };
> Then classes derived from A can use operator+ automatically, *or* they can
>override it as follows:
>     class B : public A {
> // ...
> friend B operator+ (const B& left, const B& right);
>    };

With a virtual function the actual type of the object need not be known
by the caller, only the base type.

A function(A& left, A& right)	// Suppose left was actually of type B&
{
  return left + right;		// Call the virtual member function operator+
}

This will call B::operator+ if A::operator+ is virtual and B::operator+
exists (provided the arguments match).  Of course, B::operator+ must
then take account of the actual type of its argument, but this is easy
since operator+ is commutative (isn't it? :-)

A
B::operator+(A& arg)
{
  return arg + (*this);
}

This will call A::operator+(B&), or if this is virtual and arg is a B
it will call B::operator+(B&).

David Fox

fox@allegra.att.com (David Fox) (11/28/90)

In article <rae.659682898@earth> rae@gpu.utcs.toronto.edu (Reid Ellis) writes:

>Does anyone else think that
>
> operator+(const type1 &, const type2 &);
>
>should have a return type of type1 at all times?

Perhaps it is a cop out, but my life has been much easier since
I gave up operator+ entirely and switched to operator+=.  1/2 :-)
(And I give it a return type of "void"!)

David Fox

sdm@cs.brown.edu (Scott Meyers) (11/29/90)

In article <GLENN.90Nov27181143@huxley.huxley.bitstream.com> <glenn@bitstream.com> (Glenn Parker) writes:
| In article <FOX.90Nov22085257@papyrus.tempo.nj.att.com>,
| fox@allegra.att.com (David Fox) writes:
| > Another difference is that the member version can be virtual, while
| > the friend version cannot.
| 
| Is this really a big difference?  Global overloading can make operator+
| work very much like a virtual function.  If the friend function is declared

This is really a big difference.  Overloaded function calls are resolved
based on static types, virtual function calls are resolved based on dynamic
types.

Scott

-------------------------------------------------------------------------------
What's the difference between Willie Horton and Buddy Cianci?  Willie
Horton was convicted of his felony in Massachusetts.

sking@nowhere.uucp (Steven King) (11/29/90)

   Various people have responded to various aspects of the original post; I
 think it is worth pointing out that under Cfront, for binary operators
 returning an instance of a class, one can put the body of the actual
 function in the corresponding assignment operator and define the binary
 operator as an inline that creates a temporary and assigns it the value
 of one of the binary args, then applies the assignment operator against
 the temporary and the remaining binary arg, returning the temporary.
   This DOES NOT result in a dangling reference, even if the translator can
 not inline the expression. ie.,

class point {
	double	x,y,z ;
public:
	point ( point & ) ;
	void operator += ( point & ) ;
	inline friend point &operator + ( point &, point & );
} ;

inline point &operator + ( point &a1, point &a2 )
{
	point a0 = a1 ;
	a0 += a2 ;
	return a0 ;
}

   This works, even in expressions like

	point p1, p2, p3, p4 ;

	p1 = p2 + p3 + p4 ;

 because the temporaries are created at the scope of the invoking expression.
 It will work even though the translator may not be able to inline the second
 invocation of the operator + ; it will create a static function taking three
 arguements, one of which is the temporary result.

   The biggest problem is that a lot of temporaries can be created, often
 without the programmer being aware of it, and the complexity may overwhelm
 less robust C compilers. 

	CAVEAT

   This behavior is easily demonstrated for Cfront 2.0, however I could not
 find anything in Lippman, E&S, or the Cfront manual to indicate if was
 one of those peculuarities specific to Cfront or part of the language.


-- 
 sking@nowhere			      | better to remain unseen 
..!cs.utexas.edu!ut-emx!nowhere!sking |    and be thought a fool
				      |       than to post 
				      |          and remove all doubt...

fuchs@it.uka.de (Harald Fuchs) (11/30/90)

rae@gpu.utcs.toronto.edu (Reid Ellis) writes:

>rmartin@clear.com (Bob Martin) writes:
>!foo fa,fb;
>!int ia;
>!...
>!fb=ia+fa;  // adding an int to a foo.
>!
>!If you use the foo::operator+(foo&) method, then the above statement
>!will produce a compiler error since foo::operator+(foo&) requires that
>!the left hand side of the + operator must be a foo.

>Not necessarily.  What if there existed "foo::operator int()" as well
>as "foo:foo(int)"?  Then the above would work fine.

Yes, but it would not do what you might expect. It would convert fa to
an int, perform _integer_ addition, and convert the result to a foo.
foo::operator+(foo&) would not be called at all.
--

Harald Fuchs <fuchs@it.uka.de> <fuchs%it.uka.de@relay.cs.net> ...
<fuchs@telematik.informatik.uni-karlsruhe.dbp.de>   *gulp*

jimad@microsoft.UUCP (Jim ADCOCK) (11/30/90)

Koenig has a good discussion of this and other frequent C++ questions
in November's JOOP.  He points out that symmetric operators should _be_
symmetric.  Using members:

	l + r;

is equivalent to 

	l.operator+(r);

which clarifies the assymmetry.  Using friends instead this becomes:

	operator+(l, r);

maintaining symmetry [both l and r are parameters treated similarly.]
Conversely, non-symmetric operators need not be friends.

amodeo@dataco.UUCP (Roy Amodeo) (11/30/90)

In article <rae.659682898@earth> Reid Ellis <rae@gpu.utcs.toronto.edu> writes:
>[Personal opinion follows]
>
>	I don't agree with the idea that the result of adding two
>	objects of different type should return any arbitrary type, as
>	defined by a global function "operator+(type1, type2)".
>	Rather, I have a "gut feel" that if I add an orange to an
>	apple, it is up to the apple to decide what to do with it.  I
>	guess this comes from English where, for example, you add an
>	element to a set [resulting in a new set] but you don't add a
>	set to an element..
>
>	Does anyone else think that
>
>		operator+(const type1 &, const type2 &);
>
>	should have a return type of type1 at all times?
>
>[end opinion]

Not here. If it did, it would imply that the result of
		var_of_type1 + var_of_type2
is different than
		var_of_type2 + var_of_type1

and although non-commutative addition is valid in some contexts, the natural
view of it is that addition is commutative. Instead, how about this idea:
Cast both var_of_type1 and var_of_type2 into type3 which is the minimal
type which contains all elements of type1 and type2. For instance, with
type1 as apples and type2 as oranges, type3 would be fruit. If type1
was integer and type2 was complex_real, type3 would be complex_real.
The addition expression should be performed on two values of type type3
yielding an expression of type3 which can then be cast to type1 or type2 as
desired (possibly losing accuracy).

This means that addition would always be defined for two operands of the
SAME type, requiring (possibly implicit, possibly explicit) casts to coerce
operands into the correct type. The result of the operation is always in
the supertype again requiring (possibly implicit, possibly explicit) casts
to coerce them into the desired resulting type.

The downside to this is that if you want to, say, add a scalar to a matrix,
you really don't want to convert the scalar to a matrix before performing
the addition. In cases like these, you may wish to define addition over
heterogeneous types as an optimization, but the semantics of the addition
should still behave as if it were the addition of two homogeneous types.

Beyond this difficulty, does anyone have any comments on this way of
doing things? Is it practical? Is it complete? Is it intuitive?
Is there a more "clean" approach?

rba iv
>
>					Reid
>
>--
>Reid Ellis  176 Brookbanks Drive, Toronto ON, M3A 2T5 Canada
>rae@gpu.utcs.toronto.edu      ||  rae%alias@csri.toronto.edu
>CDA0610@applelink.apple.com   ||             +1 416 446 1644

--
nrcaer!dataco!amodeo		Disclaimer:	I deny everything.

sking@nowhere.uucp (Steven King) (11/30/90)

In article <1990Nov29.023619.29838@nowhere.uucp> I wrote:

>   Various people have responded to various aspects of the original post; I
> think it is worth pointing out that under Cfront, for binary operators
> returning an instance of a class, one can put the body of the actual
> function in the corresponding assignment operator and define the binary
> operator as an inline that creates a temporary and assigns it the value
> of one of the binary args, then applies the assignment operator against
> the temporary and the remaining binary arg, returning the temporary.
>   This DOES NOT result in a dangling reference, even if the translator can
> not inline the expression. ie.,
>

   the line 

>	inline friend point &operator + ( point &, point & );

   should read

	inline friend point operator + ( point &, point & ) ;

   and the reason I couldnt find anything in the ARM, etc., is that I was
 completely confused about what was going on. time to crawl back under
 my rock.... 


   merely trying to live up to my .signature    \
                                                |
                                                v

-- 
 sking@nowhere			      | better to remain unseen 
..!cs.utexas.edu!ut-emx!nowhere!sking |    and be thought a fool
				      |       than to post 
				      |          and remove all doubt...

rmartin@clear.com (Bob Martin) (11/30/90)

In article <rae.659682898@earth> Reid Ellis <rae@gpu.utcs.toronto.edu> writes:
>rmartin@clear.com (Bob Martin) writes:
>!foo fa,fb;
>!int ia;
>!...
>!fb=ia+fa;  // adding an int to a foo.
>!
>!If you use the foo::operator+(foo&) method, then the above statement
>!will produce a compiler error since foo::operator+(foo&) requires that
>!the left hand side of the + operator must be a foo.
>
>Not necessarily.  What if there existed "foo::operator int()" as well
>as "foo:foo(int)"?  Then the above would work fine.

Granted, but then it is not using the foo::operator+(foo&) method.

>
>	Does anyone else think that
>
>		operator+(const type1 &, const type2 &);
>
>	should have a return type of type1 at all times?

Although I agree that there is a set of functions f(a,b) which
should always return elements of type a, I don't think the rule
is global.  Furthermore IMHO it should not be applied to an
operator which is expected to be commutative (like operator+).
Otherwise you are setting up scenarios where a+b != b+a.  IMHO
this would lead to support nightmares.

Consider an assembly language in which you are allowed to do
symbolic operations on relocatable (r) and absolute (a) values.
In most assemblers r+r is illegal.  r+a->r and a+r->r.  Which
is to say that adding an absolute to a relocatable always yeilds
a relocatable.  If we were to apply your rule then a+r would
have to be illegal since there is no way to return an absolute
value in this case.

For subtraction: r-a->r  a-r is illegal  r-r->a.  If we were to
apply your rule here then there would be no way to subtract two
relocatable values (A very important operation!) since the result
_must_ be absolute.

-- 
+-Robert C. Martin-----+:RRR:::CCC:M:::::M:| Nobody is responsible for |
| rmartin@clear.com    |:R::R:C::::M:M:M:M:| my words but me.  I want  |
| uunet!clrcom!rmartin |:RRR::C::::M::M::M:| all the credit, and all   |
+----------------------+:R::R::CCC:M:::::M:| the blame.  So there.     |

chip@tct.uucp (Chip Salzenberg) (12/01/90)

According to fox@allegra.att.com (David Fox):
>Perhaps it is a cop out, but my life has been much easier since
>I gave up operator+ entirely and switched to operator+=.  1/2 :-)

No smileys -- this is *excellent* advice.  I tossed concatenation out
of my String class, and never noticed the loss.  Except now my code
compiles faster and smaller.

Hint: Define "operator +=" in terms of a basic "append" member
function.  Sometimes you'll want to append random bytes from the
middle of a random buffer...
-- 
Chip Salzenberg at Teltronics/TCT     <chip@tct.uucp>, <uunet!pdn!tct!chip>
      "I'm really sorry I feel this need to insult some people..."
            -- John F. Haugh II    (He thinks HE'S sorry?)

Reid Ellis <rae@gpu.utcs.toronto.edu> (12/09/90)

I had said:
>	Does anyone else think that
>
>		operator+(const type1 &, const type2 &);
>
>	should have a return type of type1 at all times?


To which Bob Martin <rmartin@clear.com> replied:
>Although I agree that there is a set of functions f(a,b) which should
>always return elements of type a, I don't think the rule is global.
>Furthermore IMHO it should not be applied to an operator which is
>expected to be commutative (like operator+).  Otherwise you are
>setting up scenarios where a+b != b+a.  IMHO this would lead to
>support nightmares.

I'd have to agree with you.  We've been working with points and vectors
and have found that the following are useful operations:

vector = point  - point	 |  vector point::operator-(const point &) const
point  = point  + vector |  point  point::operator+(cont vector &) const
point  = vector + point	 |  point  vector::operator+(const point &p) const

So much for my statement. :-)

Note that point+point is ilegal.  Also note that [conceptually]
vector-point could be considered illegal whereas point-vector might
not since unary- on a vector has a coordinate-frame-independant
meaning, whereas unary- on a point is specific to the frame of
reference, if valid at all.

But still, it remains whether it is "better" to have friend functions
or methods to handle these cases.  I am reluctant to use friends,
mostly because I can't be selective about how much they know.  It's an
all-or-nothing thing.

One way to avaoid friends is to define the operator in both classes
but define one as calling the other.  For instance, in the last case
above, we could define "vector+point" as this:

point vector::operator+(const point &p) const { return point + *this; }

and have all the "real" code in "point::operator+(const vector &)".
Note that this makes it very important that any "operator+" type
methods that return values on the stack be declared "const".

Actually, for this case you would need two friend functions anyways,
but you get the idea.

					Reid
--
Reid Ellis  176 Brookbanks Drive, Toronto ON, M3A 2T5 Canada
rae@gpu.utcs.toronto.edu      ||  rae%alias@csri.toronto.edu
CDA0610@applelink.apple.com   ||             +1 416 446 1644