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
- Why are many operators better as non-member functions?
- What does defaulted operator== do in C++20?
Show answers
- To allow implicit conversions on both operands and avoid asymmetric member lookup constraints.
- Generates a member-wise equality check.
Exercises
- Implement operator-, operator-=, and a scalar multiplication operator for Vec.
- Add defaulted comparisons to a small aggregate and test sorting in a std::vector.