C++ - Operator Overloading Deep Dive

Overview

Estimated time: 60–80 minutes

Implement common operator overloads correctly and safely. Cover equality, ordering (including C++20 spaceship), arithmetic and compound assignments, stream operators, and indexing with const/non-const forms.

Learning Objectives

  • Implement operator== and ordering consistently; understand defaulted comparisons (C++20).
  • Write arithmetic and compound operators as non-member friends where appropriate.
  • Implement stream insertion/extraction and operator[] with const/non-const overloads.

Prerequisites

Equality and ordering

#include <compare>
struct Point {
  int x{0}, y{0};
  bool operator==(const Point&) const = default;           // C++20
  std::strong_ordering operator<=>(const Point&) const = default; // C++20
};

Arithmetic and compound

#include <iostream>
struct Vec {
  int x{0}, y{0};
};
// non-members preserve implicit conversions on both operands
Vec operator+(Vec a, const Vec& b){ a.x+=b.x; a.y+=b.y; return a; }
Vec& operator+=(Vec& a, const Vec& b){ a.x+=b.x; a.y+=b.y; return a; }
std::ostream& operator<<(std::ostream& os, const Vec& v){ return os << v.x << "," << v.y; }
int main(){ Vec a{1,2}, b{3,4}; a+=b; std::cout << (a+b) << "\n"; }

Expected Output: 7,10

Indexing operator

#include <cstddef>
#include <stdexcept>
struct Buffer {
  int data[3]{1,2,3};
  int& operator[](std::size_t i){ if(i>=3) throw std::out_of_range("oob"); return data[i]; }
  const int& operator[](std::size_t i) const { if(i>=3) throw std::out_of_range("oob"); return data[i]; }
};

Explicit conversions

struct SafeInt {
  int value{0};
  explicit operator bool() const { return value != 0; } // avoids unintended if(safeInt) conversions
};

Beginner Boosters

#include 
#include 
#include 
struct Point { int x{0}, y{0}; bool operator==(const Point&) const = default; };
std::ostream& operator<<(std::ostream& os, const Point& p){ return os << '(' << p.x << ',' << p.y << ')'; }
int main(){
  std::vector v{{2,1},{0,0},{2,1}};
  std::sort(v.begin(), v.end(), [](const Point& a, const Point& b){ return a.x < b.x || (a.x==b.x && a.y

Expected Output (example): (0,0) (2,1) (2,1)

Common Pitfalls

  • Breaking symmetry or violating mathematical expectations (e.g., a+b != b+a without good reason).
  • Making operator<< a member; prefer free function to allow implicit conversions on left operand (std::ostream).
  • Forgetting const correctness on operator[] and accessors.

Checks for Understanding

  1. Why are many operators better as non-member functions?
  2. What does defaulted operator== do in C++20?
Show answers
  1. To allow implicit conversions on both operands and avoid asymmetric member lookup constraints.
  2. Generates a member-wise equality check.

Exercises

  1. Implement operator-, operator-=, and a scalar multiplication operator for Vec.
  2. Add defaulted comparisons to a small aggregate and test sorting in a std::vector.