Operator Overloading
You can give your own types the natural operators: + for adding vectors, == and < for comparing dates, << for printing. Done well, operator overloading makes user-defined types as convenient as built-in ones.
Arithmetic operators
#include <iostream> struct Vec2 { double x = 0, y = 0; Vec2& operator+=(const Vec2& other) { // compound assignment: a member x += other.x; y += other.y; return *this; } }; Vec2 operator+(Vec2 a, const Vec2& b) { return a += b; } // built on += Vec2 operator*(const Vec2& v, double k) { return {v.x * k, v.y * k}; } Vec2 operator-(const Vec2& v) { return {-v.x, -v.y}; } // unary minus int main() { Vec2 position{1, 2}, velocity{0.5, -0.5}; position += velocity * 2; Vec2 back = -position; std::cout << position.x << ',' << position.y << " " << back.x << ',' << back.y << '\n'; Vec2 sum = Vec2{1, 1} + Vec2{2, 3}; std::cout << sum.x << ',' << sum.y << '\n'; }
2,1 -2,-1 3,4
The usual pattern: implement += as a member, then write + as a free function that uses it. A free operator+ treats both sides equally, so conversions work on either operand.
Printing with <<
To print with std::cout, overload operator<< for std::ostream. It must be a free function (the stream is on the left) and should return the stream so output can be chained:
#include <iostream> struct Money { long cents; }; std::ostream& operator<<(std::ostream& out, const Money& m) { return out << '$' << m.cents / 100 << '.' << (m.cents % 100 < 10 ? "0" : "") << m.cents % 100; } int main() { Money price{1999}, tip{305}; std::cout << "price " << price << ", tip " << tip << '\n'; }
price $19.99, tip $3.05
Comparisons with <=>
C++20 makes comparisons easy. Defaulting operator<=> compares members in order and gives you <, <=, > and >=; defaulting it also provides == and !=:
#include <algorithm> #include <compare> #include <iostream> #include <vector> struct Date { int year, month, day; auto operator<=>(const Date&) const = default; }; std::ostream& operator<<(std::ostream& out, const Date& d) { return out << d.year << '-' << d.month << '-' << d.day; } int main() { std::vector<Date> dates{{2026, 3, 14}, {2025, 12, 1}, {2026, 1, 30}}; std::sort(dates.begin(), dates.end()); for (const auto& d : dates) std::cout << d << " "; std::cout << '\n' << std::boolalpha << (Date{2026, 1, 1} < Date{2026, 1, 2}) << ' ' << (dates[0] == Date{2025, 12, 1}) << '\n'; }
2025-12-1 2026-1-30 2026-3-14 true true
When the default order isn't right, write operator<=> yourself and return a comparison of the relevant parts.
Subscript and call operators
operator[] gives indexing; operator() makes an object callable — a function object, which can carry state:
#include <algorithm> #include <iostream> #include <vector> class Matrix { public: Matrix(int rows, int cols) : cols_(cols), data_(rows * cols) {} double& operator()(int r, int c) { return data_[r * cols_ + c]; } double operator()(int r, int c) const { return data_[r * cols_ + c]; } private: int cols_; std::vector<double> data_; }; struct IsMultipleOf { int n; bool operator()(int x) const { return x % n == 0; } }; int main() { Matrix m(2, 3); m(1, 2) = 7.5; std::cout << m(1, 2) << ' ' << m(0, 0) << '\n'; std::vector<int> v{3, 6, 7, 9, 10, 12}; std::cout << std::count_if(v.begin(), v.end(), IsMultipleOf{3}) << '\n'; }
7.5 0 4
Lambdas (lesson 24) are just a short way of writing function objects like IsMultipleOf. (C++23 also allows operator[] with several indexes.)
Guidelines
- Keep the usual meaning.
+should add,==should compare for equality. Surprising operators make code unreadable. - Overload related operators together — if you have
+, also provide+=; prefer defaulted<=>for comparisons. - Return the right things: compound assignment returns
*thisby reference; arithmetic returns a new value;<<returns the stream. - Some operators should almost never be overloaded:
&&,||and,lose their short-circuit and ordering rules.
Try it yourself
Give a Fraction type +, *, == and <<.
5/6 1/6 true