views:

500

answers:

4

Hi,

Could anybody tell me

the difference between operator overloading using the friend keyword and as a member function inside a class?

also what is the difference incase of any unary operator overloading i.e., as a friend and as a member function

+2  A: 

The difference is that the friended function is actually in global scope, so you do not need to be an instance of the class to have access to it.

Jacob Relkin
+5  A: 

Jacob is correct… a friend function declared within a class has access to that class, but it's not inside the class at all, and everyone else has access to it.

For an operator overload which is not a member of the class (also called a free function, it may be a friend, or maybe not), the arguments are the same as the operands. For one which is a member of a class, the first operand is the "implicit argument" which becomes this.

The implicit argument is different from the first argument to a free function in a few ways:

  • Its type is reference-to-class, whereas the free function can declare any type for its first argument.
  • It does not participate in implicit type conversion. (It will not be a temporary initialized by a conversion constructor.)
  • It does participate in virtual override resolution. (A virtual overload will be chosen by the dynamic type of the first operand, which is not possible with free functions without extra code.)

The situation is the same for unary, binary, or n-ary (in the case of operator()).

Members privilege of mutation: Operators which change the first operand (eg +=, =, prefix ++) should be implemented as member functions, and should exclusively implement the guts of all overloads. Postfix ++ is a second-class citizen; it is implemented as Obj ret = *this; ++ this; return ret;. Note that this sometimes extends to copy-constructors, which may contain *this = initializer.

Rule of freedom for commuters: Only commutative operators (eg /) should be free functions; all other operators (eg unary anything) should be members. Commutative operators inherently make a copy of the object; they are implemented as Obj ret = lhs; ret @= rhs; return ret; where @ is the commutative operator and lhs and rhs are left-hand side and right-hand side arguments, respectively.

Golden rule of C++ friendship: Avoid friendship. friend pollutes the semantics of a design. Overloading corollary: Overloading is simple if you follow the above rules, then friend is harmless. friending boilerplate overload definitions allows them to be placed inside the class { braces.

Note that some operators cannot be free functions: =, ->, [], and (), because the standard specifically says so in section 13.5. I think that's all… I thought unary & and * were too, but I was apparently wrong. They should always be overloaded as members, though, and only after careful thought!

Potatoswatter
why unary operators cannot be friends?
cpp_Beginner
Because there is no need for them to be. Friend operators are permitted to enable you to put a class type on the right of a binary operator. Since unary operators don't have a right side, you don't need them to be friends.
Stewart
@cpp: Sorry, I mis-remembered the standard. Never mind.
Potatoswatter
+1 for the general answer, even if I would rewrite the rule of thumb as: *Commutative operators should be free functions (preferably **not** `friend`s)* Friendship is the most coupling relationship and should be avoided (i.e. define `operatorX=` as member, and define `operatorX` in terms of `operatorX=`)
David Rodríguez - dribeas
@David: That is true. I think friendship between classes is harmful. But making binary ops friends allows definition inside the `class` block, which I like, and actually loosening access to avoid friendship with a binary op would likely be destructive—although actually needing friendship would be a reason to stop and think.
Potatoswatter
In general you do not need to loosen the access. If you defined `operator+=` as a member function (which is not required, but seems appropriate being some sort of assignment) then you can define `X operator+( X lhs, X const }` without loosening access. On the other hand, the advantage (I have never found it to be a disadvantage) of defining free functions (must be friends) inside the class braces is that they won't take part in that type (that is, the compiler will not consider performing implicit conversions on both arguments to call the function)
David Rodríguez - dribeas
@David: I agree, should change it. But first… what's that about avoiding double implicit conversion? That requires both operands without a native operator, right? And why should the location of definition matter, or is this empirical?
Potatoswatter
I read it some time ago, but I cannot find the quote. I have briefly browsed the standard and the closest I can find is 11.4/5 *A friend function defined in a class is in the (lexical) scope of the class in which it is defined*, but I am not really sure of the whole implications of 11.4/5 because it also says that the function belongs to the enclosing namespace... I am kind of unsure, but it empirically works...
David Rodríguez - dribeas
+1  A: 

A member function requires that the left hand operator must be of that type. A friend function can allow implicit casting on the left hand operator.

So for example, lets say we create a BigInt class. And we create a member function operator + to take a right hand operator of BigInt.

Now lets also say BigInt has a constructor that takes a regular int. This constructor is NOT explicit (explicit keyword) and it takes one parameter. That means C++ can implitily convert from int to BigInt.

When we have these things we can do this:

BigInt foo( 5 ); BigInt bar; bar = foo + 5;

But we CAN'T do this:

BigInt foo( 5 ) BigInt bar; bar = 5 + foo;

However, if we used a friend function instead of a member function then both will work.

ProgramMax
A: 

Member functions can be called on rvalues, whereas free functions accepting references to non-const cannot be called with rvalues. For example, ++function_returning_iterator_by_value() only compiles if you implement operator++ as a member.

FredOverflow