Operator Overloading
2 min read
Operator Overloading
C++ lets you define the behavior of operators (+, ==, <<, [], etc.) for your own types. Done well, operator overloading makes user-defined types as natural to use as built-in ones.
Arithmetic operators
#include <iostream>
struct Vec2 {
double x, y;
Vec2 operator+(const Vec2& rhs) const {
return {x + rhs.x, y + rhs.y};
}
Vec2 operator*(double scalar) const {
return {x * scalar, y * scalar};
}
Vec2& operator+=(const Vec2& rhs) {
x += rhs.x;
y += rhs.y;
return *this;
}
};
int main() {
Vec2 a = {1.0, 2.0};
Vec2 b = {3.0, 4.0};
Vec2 c = a + b;
std::cout << c.x << " " << c.y << "\n"; // 4 6
Vec2 d = a * 3.0;
std::cout << d.x << " " << d.y << "\n"; // 3 6
}
Comparison operators
#include <string>
struct Employee {
std::string name;
int id;
bool operator==(const Employee& rhs) const { return id == rhs.id; }
bool operator!=(const Employee& rhs) const { return id != rhs.id; }
bool operator< (const Employee& rhs) const { return id < rhs.id; }
};
C++20 introduces the spaceship operator <=> which generates all six comparisons from one definition:
#include <compare>
struct Employee {
std::string name;
int id;
auto operator<=>(const Employee& rhs) const = default;
bool operator==(const Employee& rhs) const = default;
};
Stream insertion operator
Overload << as a free function to integrate with std::cout:
#include <iostream>
struct Vec2 { double x, y; };
std::ostream& operator<<(std::ostream& os, const Vec2& v) {
return os << "(" << v.x << ", " << v.y << ")";
}
int main() {
Vec2 v = {1.5, 2.5};
std::cout << v << "\n"; // (1.5, 2.5)
}
Return std::ostream& so that chained << calls work: std::cout << a << b.
Subscript operator
#include <stdexcept>
#include <vector>
class Matrix {
std::vector<double> data;
int rows_, cols_;
public:
Matrix(int r, int c) : data(r * c, 0.0), rows_(r), cols_(c) {}
double& operator()(int r, int c) {
return data[r * cols_ + c];
}
double operator()(int r, int c) const {
return data[r * cols_ + c];
}
};
int main() {
Matrix m(3, 4);
m(1, 2) = 9.5;
std::cout << m(1, 2) << "\n"; // 9.5
}
Note: for 2D access [][] does not work directly in C++23 and earlier. Use () for multi-dimensional indexing, or a proxy object. C++23 adds multi-dimensional [].
Increment and decrement
Prefix and postfix versions have different signatures:
struct Counter {
int value = 0;
Counter& operator++() { // prefix ++c
value++;
return *this;
}
Counter operator++(int) { // postfix c++ (the int is a dummy)
Counter tmp = *this;
value++;
return tmp;
}
};
Rules of thumb
- Overload only when it makes the type feel natural to use (
+for numeric types,<<for printable types). - Do not overload operators with unintuitive meanings (
*for a string type,+for a database connection). - Arithmetic operators that produce a new value (
+,-) should beconstmember functions or free functions. - Compound assignment operators (
+=,-=) should modify*thisand return*thisby reference.
Learn modern C++ through working code. Each chapter introduces one concept — variables, functions, classes, templates, smart pointers, concurrency — with a clear example, a line-by-line explanation, and notes on how it applies in real programs.
View book →