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.eduBruce.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 1644glenn@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