Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why define operator + or += outside a class, and how to do it properly?

I am a bit confused about the differences between

Type  operator +  (const Type &type); Type &operator += (const Type &type); 

and

friend Type  operator +  (const Type &type1, const Type &type2); friend Type &operator += (const Type &type1, const Type &type2); 

which way is preferred, what do they look like and when should either be used?

like image 309
Dave Avatar asked Jan 11 '11 00:01

Dave


People also ask

What is the purpose of using operator?

An operator is used to manipulate individual data items and return a result. These items are called operands or arguments. Operators are represented by special characters or by keywords.

What is operator overloading Why is it necessary to overload an operator?

The purpose of operator overloading is to provide a special meaning of an operator for a user-defined data type. With the help of operator overloading, you can redefine the majority of the C++ operators. You can also use operator overloading to perform different operations using one operator.

Why would you overload operators for your classes?

It allows you to provide an intuitive interface to users of your class, plus makes it possible for templates to work equally well with classes and built-in/intrinsic types. Operator overloading allows C/C++ operators to have user-defined meanings on user-defined types (classes).


2 Answers

The first form of the operators is what you would define inside class Type.

The second form of the operators is what you would define as free-standing functions in the same namespace as class Type.

It's a very good idea to define free-standing functions because then the operands to those can take part in implicit conversions.

Example

Assume this class:

class Type {     public:     Type(int foo) { }      // Added the const qualifier as an update: see end of answer     Type operator + (const Type& type) const { return *this; } }; 

You could then write:

Type a = Type(1) + Type(2); // OK Type b = Type(1) + 2; // Also OK: conversion of int(2) to Type 

But you could NOT write:

Type c = 1 + Type(2); // DOES NOT COMPILE 

Having operator+ as a free function allows the last case as well.

What the second form of the operator does wrong though is that it performs the addition by directly tweaking the private members of its operands (I 'm assuming that, otherwise it would not need to be a friend). It should not be doing that: instead, the operators should also be defined inside the class and the free-standing functions should call them.

To see how that would turn out, let's ask for the services of a guru: http://www.gotw.ca/gotw/004.htm. Scroll at the very end to see how to implement the free-standing functions.

Update:

As James McNellis calls out in his comment, the two forms given also have another difference: the left-hand-side is not const-qualified in the first version. Since the operands of operator+ should really not be modified as part of the addition, it's a very very good idea to const-qualify them all the time. The class Type in my example now does this, where initially it did not.

Conclusion

The best way to deal with operators + and += is:

  1. Define operator+= as T& T::operator+=(const T&); inside your class. This is where the addition would be implemented.
  2. Define operator+ as T T::operator+(const T&) const; inside your class. This operator would be implemented in terms of the previous one.
  3. Provide a free function T operator+(const T&, const T&); outside the class, but inside the same namespace. This function would call the member operator+ to do the work.

You can omit step 2 and have the free function call T::operator+= directly, but as a matter of personal preference I 'd want to keep all of the addition logic inside the class.

like image 102
Jon Avatar answered Sep 28 '22 04:09

Jon


The proper way to implement operators, with respect to C++03 and C++0x (NRVO and move-semantics), is:

struct foo {     // mutates left-operand => member-function     foo& operator+=(const foo& other)     {         x += other.x;          return *this;     }      int x; };  // non-mutating => non-member function foo operator+(foo first, // parameter as value, move-construct (or elide)                 const foo& second)  {     first += second; // implement in terms of mutating operator      return first; // NRVO (or move-construct) } 

Note it's tempting to combine the above into:

foo operator+(foo first, const foo& second)  {     return first += second; } 

But sometimes (in my testing) the compiler doesn't enable NRVO (or move semantics) because it can't be certain (until it inlines the mutating operator) that first += second is the same as first. Simpler and safer is to split it up.

like image 43
GManNickG Avatar answered Sep 28 '22 03:09

GManNickG