Operator overloading in C++

Operators to overload

  • Assignment operator
  • Bitwise shift operators
  • Function call operator
  • Comparison operators
  • Arithmetic operators
  • Array subscripting operators
  • Pointer operators

Assignment operator

You only need to implement the assignment operator if you need to implement the copy constructor and destructor – and vice-versa.
Use the copy-and-swap idiom to implement the assignment operator:

  1. Take other by value – this invokes the copy constructor
  2. Swap the contents of this with other – you need to implement swap
  3. other is destroyed at the end of the function, taking the old state with it
X& X::operator=(X other)
{
  swap(other);
  return *this;
}

Note that this does not do a self-assignment check, but self-assignment is guaranteed to be harmless.

Bitwise shift operators

These are most commonly overloaded to insert into and extract from iostreams, or some other sort of stream.
They need to be friend functions if they need to access private members, as they can’t be members themselves.

When used with iostreams, these functions need to take the stream by non-const reference (iostreams cannot be copied and will be modified) and return it by reference.

std::ostream& operator<<(std::ostream& os, const T& obj)
{
  // Write the object to the stream
  return os;
}

std::istream& operator>>(std::istream& is, T& obj)
{
  // Read the object from the stream
  return is;
}

Function call operator

Use the function all operator to make a functor.
This is always a member function.
It can be overloaded to take any number and type of arguments.
It can return any type, or void.
Functors are often copied, so the class must be copyable and inexpensive to copy.

Comparison operators

These should be implemented as non-member functions.
Missing operators are not automatically deduced from the others.
The standard library only uses < (strict weak ordering), but you should overload them all for consistency.

You only need to fully implement operator== and operator<, as the others can be implemented in terms of them. They also make your class model the EqualityComparable and LessThanComparable concepts.

bool operator ==(const X& lhs, const X& rhs)
{
    // Do actual comparison
}
bool operator < (const X& lhs, const X& rhs)
{
    // Do actual comparison
}
bool operator !=(const X& lhs, const X& rhs)
{
    return !operator==(lhs, rhs);
}
bool operator > (const X& lhs, const X& rhs)
{
    return operator< (rhs, lhs);
}
bool operator <=(const X& lhs, const X& rhs)
{
    return !operator> (lhs, rhs);
}
bool operator >=(const X& lhs, const X& rhs)
{
    return !operator< (lhs, rhs);
}

Arithmetic operators

Unary arithmetic operators

If you overload the prefix form, you should also overload the postfix form, and vice-versa.

class X
{
    X& operator++()
    {
        // Do actual increment
        return *this;
    }

The postfix form is distinguished by a dummy int argument.
You need to use a temporary to save the old value before incrementing it so that the old value can be returned.

    X operator++(int)
    {
       X temp(*this);
       operator++();
       return temp;
    }
};

Binary arithmetic operators

You should overload op and op=.
op= is a member while op is a non-member.
You can implement op in terms of op=.
op needs to take its lhs by copy so it can modify and return it without modifying the original.

class X
{
    X& operator+=(const X& rhs)
    {
        // Do actual addition of rhs to *this
        return *this;
    }
};

X operator+(X lhs, const X& rhs)
{
    lhs += rhs;
    return lhs;
}

Array subscripting operators

These must be members. They can only take 1 argument (although it can be of any type).
You should implement const and non-const versions to allow const overloading.

class X 
{
    value_type& operator[](size_type index);
    const value_type& operator[](size_type index) const;
};

Pointer operators

This is how you implement a smart pointer.
Overload the -> and * operators.
You should implement const and non-const versions to allow const overloading.

class SmartPtr 
{
    value_type& operator*();
    const value_type& operator*() const;
    value_type* operator->();
    const value_type* operator->() const;
};

Related

Default operator== in C++