Programming in C++

Object Oriented Programming (OOP)

Overloading Operators for User Defined Types

Gerald Senarclens de Grancy

Where to Declare / Define Operators

Operators having an object of a user defined type on their left hand side may be declared and defined inside in that class / struct

All other operators have to be declared and defined outside

Declaration and Definition Inside Data Type

Advantages
Sometimes conciseness
Possibility to cleanly access private members
Disadvantages
Limited flexibility for binary operators
Can't handle mixed types easily

Declaration and Definition Outside Data Type

Advantages
Consistent declaration of operators with two different operand types
Handles mixed types easier (templates)
More consistent with overloading operators for built-in types
Disadvantages
Access to private members would require friend declaration
Sometimes less concise

Related Conventions

C.over
Overloading and overloaded operators
C.168
Define overloaded operators in the namespace of their operands
C.86
Make == symmetric with respect to operand types and noexcept

Syntax

Definition Inside Class (Struct)

class Type {
public:
  // examples of binary operators defined inside class
  // both operands are class members; LHS is *this, RHS is sole argument
  Type operator+(const Type& other) const noexcept { ... }
  // only the LHS operand is a class member, RHS is sole argument
  Type operator+(int i) const noexcept { ... }
  // unary operator; just uses *this -> no arguments
  Type operator-() const { ... }

  // ...
};

Standalone Definition

No access to *this => all operands have to be passed as arguments

const may only be used for arguments as there is no *this

// both operands are class members
Type operator+(const Type& first, const Type& second) noexcept { ... }
// only the LHS operand is a class member
Type operator+(const Type& t, int i) noexcept { ... }
// unary operator
Type operator-(const Type& t) { ... }

// the following operators have to be defined outside of class / struct
// only the RHS operand is a class member
Type operator+(int i, const Type& t) noexcept { ... }
// the output stream operator cannot be defined in a class / struct
std::ostream& operator+(std::ostream& out, const Type& t) noexcept { ... }

Example: Complex Numbers

// ... header guard etc
#include <iostream>

class Complex {
public:
  explicit Complex(double real, double imag) : real_{real}, imag_{imag} {}
  double real() const { return real_; }
  double imag() const { return imag_; }
  Complex operator+(const Complex& c) const;
  Complex operator+(double r) const { return Complex{real_ + r, imag_}; }
private:
  double real_ = 0.0;
  double imag_ = 0.0;
};
Complex operator+(double r, const Complex& c);  // cannot be declared in class
std::ostream& operator<<(std::ostream& out, const Complex& c);
// ...
Download complex_numbers.hpp
#include "complex_numbers.hpp"

#include <iostream>
// ...

Complex Complex::operator+(const Complex& c) const {
  return Complex{real_ + c.real(), imag_ + c.imag()};
}

Complex operator+(double r, const Complex& c) {
  return c + r;
}

std::ostream& operator<<(std::ostream& out, const Complex& c) {
  out << c.real() << (c.imag() >= 0 ? "+" : "") << c.imag() << + "j";
  return out;
}
Download complex_numbers.cpp
#include "complex_numbers.hpp"

#include <iostream>

int main() {
  Complex c1{5, 3};
  Complex c2{3, -2};
  std::cout << "c1: " << c1 << ", c2: " << c2 << std::endl;
  std::cout << "c1+c2: " << c1 + c2 << ", c2+c1: " << c2 + c1 << std::endl;
  std::cout << "c1+3: " << c1 + 3 << ", 3+c1: " << 3 + c1 << std::endl;
  return 0;
}
Download complex_main.cpp

Exercise

Please solve the complex numbers exercise on exercism

Questions
and feedback...