Modern examples of C++ operator overloading: custom operators examples that actually matter
Let’s start where most developers actually feel the benefit: making domain types behave like the built‑ins they represent.
1. Overloading arithmetic operators for a 2D vector
A classic example of C++ operator overloading is a small math type like a 2D vector. Instead of writing add(v1, v2) everywhere, you can use +, -, and * in a readable way.
#include <cmath>
#include <iostream>
struct Vec2 {
double x{};
double y{};
// Vector addition
Vec2 operator+(const Vec2& other) const {
return {x + other.x, y + other.y};
}
// Vector subtraction
Vec2 operator-(const Vec2& other) const {
return {x - other.x, y - other.y};
}
// Scalar multiplication (Vec2 * scalar)
Vec2 operator*(double scalar) const {
return {x * scalar, y * scalar};
}
// Compound assignment
Vec2& operator+=(const Vec2& other) {
x += other.x;
y += other.y;
return *this;
}
};
// Scalar * Vec2 (non-member to allow implicit conversion on left-hand side)
inline Vec2 operator*(double scalar, const Vec2& v) {
return v * scalar;
}
int main() {
Vec2 a{1.0, 2.0};
Vec2 b{3.0, 4.0};
Vec2 c = a + b; // uses operator+
Vec2 d = 2.0 * c; // uses non-member operator*
c += d; // uses operator+=
std::cout << c.x << ", " << c.y << '\n';
}
This is one of the best examples of C++ operator overloading: custom operators examples that genuinely improve readability. The code expresses math, not plumbing.
2. Strongly typed money with comparison and arithmetic
Floating‑point for money is a bad idea. A better approach is a Money type using integers (cents) and overloaded operators. This is a very practical example of C++ operator overloading used in finance and e‑commerce systems.
#include <compare>
#include <cstdint>
class Money {
public:
explicit Money(std::int64_t cents = 0) : cents_(cents) {}
// Arithmetic
Money operator+(const Money& other) const {
return Money(cents_ + other.cents_);
}
Money operator-(const Money& other) const {
return Money(cents_ - other.cents_);
}
Money& operator+=(const Money& other) {
cents_ += other.cents_;
return *this;
}
// C++20 three-way comparison
auto operator<=>(const Money&) const = default;
private:
std::int64_t cents_;
};
int main() {
Money price(1999); // $19.99 in cents
Money tax(160); // $1.60
Money total = price + tax;
if (total > Money(2000)) {
// ...
}
}
Here, operator+, operator-, and the comparison operator make Money feel like a numeric type while still being type‑safe. This is a clean, domain‑driven example of C++ operator overloading: custom operators examples used in real business code.
3. Smart pointer‑like behavior with operator* and operator->
You should almost always use std::unique_ptr or std::shared_ptr instead of writing your own smart pointer. But understanding how they work is still valuable.
#include <utility>
template <typename T>
class OwningPtr {
public:
explicit OwningPtr(T* ptr = nullptr) noexcept : ptr_(ptr) {}
~OwningPtr() { delete ptr_; }
OwningPtr(const OwningPtr&) = delete;
OwningPtr& operator=(const OwningPtr&) = delete;
OwningPtr(OwningPtr&& other) noexcept : ptr_(other.ptr_) {
other.ptr_ = nullptr;
}
OwningPtr& operator=(OwningPtr&& other) noexcept {
if (this != &other) {
delete ptr_;
ptr_ = other.ptr_;
other.ptr_ = nullptr;
}
return *this;
}
T& operator*() const noexcept { return *ptr_; }
T* operator->() const noexcept { return ptr_; }
explicit operator bool() const noexcept { return ptr_ != nullptr; }
private:
T* ptr_;
};
struct Widget {
void ping();
};
int main() {
OwningPtr<Widget> w(new Widget{});
if (w) {
w->ping(); // operator->
(*w).ping(); // operator*
}
}
This is one of those examples of C++ operator overloading: custom operators examples that mimic standard library behavior. You get pointer‑like syntax while still enforcing ownership rules.
4. Safe string wrapper with operator+
String concatenation is a classic operator overloading use case. Here’s a thin wrapper around std::string that restricts implicit conversions and still supports +.
#include <string>
#include <utility>
class SafeString {
public:
SafeString() = default;
explicit SafeString(std::string s) : data_(std::move(s)) {}
const std::string& str() const noexcept { return data_; }
SafeString operator+(const SafeString& other) const {
SafeString result;
result.data_ = data_ + other.data_;
return result;
}
private:
std::string data_;
};
int main() {
SafeString first{"Hello"};
SafeString second{" World"};
SafeString message = first + second; // operator+
}
There are better choices in production (just use std::string unless you have a reason not to), but as an example of C++ operator overloading it’s straightforward and maps to how developers already think about strings.
5. Overloading operator[] for bounds‑checked access
Containers are a natural fit for operator overloading. When you want array‑like syntax with custom behavior (for example, bounds checking or logging), operator[] is your friend.
#include <cstddef>
#include <stdexcept>
#include <vector>
class SafeIntArray {
public:
explicit SafeIntArray(std::size_t size) : data_(size) {}
int& operator[](std::size_t index) {
if (index >= data_.size()) {
throw std::out_of_range("index out of range");
}
return data_[index];
}
const int& operator[](std::size_t index) const {
if (index >= data_.size()) {
throw std::out_of_range("index out of range");
}
return data_[index];
}
std::size_t size() const noexcept { return data_.size(); }
private:
std::vector<int> data_;
};
int main() {
SafeIntArray arr(10);
arr[0] = 42; // operator[]
int value = arr[0];
}
This is one of the best examples of C++ operator overloading: custom operators examples that add safety without changing how the code looks at the call site.
6. User‑defined literals for domain units (C++11+)
User‑defined literals are technically a different syntax, but they are still operator overloading under the hood. They’re heavily used in modern libraries, including time and units libraries.
#include <cstdint>
#include <iostream>
class Meters {
public:
explicit Meters(long double value) : value_(value) {}
long double value() const noexcept { return value_; }
private:
long double value_;
};
// User-defined literal for meters
Meters operator"" _m(long double value) {
return Meters(value);
}
int main() {
Meters distance = 100.0_m; // operator""_m
std::cout << distance.value() << '\n';
}
This is a modern example of C++ operator overloading: custom operators examples that improve expressiveness. Your code starts to look like the problem domain: 100.0_m, 5.0_s, 3.0_kg, and so on.
7. Overloading operator<< for logging and debugging
Streaming to std::ostream is one of the most common real examples of C++ operator overloading used in production.
#include <iostream>
#include <string>
struct User {
std::string name;
int age{};
};
std::ostream& operator<<(std::ostream& os, const User& user) {
return os << "User{name=" << user.name << ", age=" << user.age << "}";
}
int main() {
User u{"Alice", 30};
std::cout << u << '\n'; // operator<<
}
This pattern is everywhere: logging, debugging, serialization helpers. It’s probably the first example of C++ operator overloading you’ll see in any sizable codebase.
8. Overloading ++ and -- for iterator‑like types
If you build your own iterator or iterator‑like type, operator++ and operator* give you the standard iteration syntax.
#include <cstddef>
#include <iterator>
class Counter {
public:
using value_type = int;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
explicit Counter(int value = 0) : value_(value) {}
int operator*() const { return value_; }
Counter& operator++() { // prefix ++
++value_;
return *this;
}
Counter operator++(int) { // postfix ++
Counter temp = *this;
++(*this);
return temp;
}
bool operator==(const Counter& other) const { return value_ == other.value_; }
bool operator!=(const Counter& other) const { return !(*this == other); }
private:
int value_;
};
int main() {
for (Counter c{0}; c != Counter{3}; ++c) {
int v = *c; // operator*
// ...
}
}
Again, this is one of those examples of C++ operator overloading: custom operators examples that align your custom types with the expectations of the STL and range‑based algorithms.
When operator overloading is a good idea (and when it isn’t)
By 2024–2025, the C++ community has largely converged on some shared norms about operator overloading:
- Use it when the meaning of the operator is obvious from math or existing standard types.
- Avoid “cute” overloads that surprise readers (for example, using
operator+to do logging, oroperator[]to perform network calls). - Prefer non‑member overloads when you want implicit conversions on the left‑hand side, like
scalar * vector. - Keep operations cheap and predictable; if
+suddenly allocates a file handle or hits a database, that’s confusing.
Modern guidelines from organizations like the ISO C++ committee and large industrial users (Google, Microsoft, etc.) all point in the same direction: use operator overloading to make code clearer, not just shorter.
Modern style tips for cleaner custom operators
A few quick patterns that show up across the best examples of C++ operator overloading:
- Mark single‑argument constructors
explicitunless you really want implicit conversions. - Use
= defaultand= deleteto control automatically generated operators and special member functions. - For comparison, prefer C++20’s
operator<=>plus= defaultwhen possible. - For containers, provide both const and non‑const
operator[]. - For heavy operations, consider named functions instead of operators; clarity wins.
If you want more in‑depth style guidance, the free C++ Core Guidelines maintained by the ISO C++ community and Microsoft are worth a read:
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
FAQ: examples of C++ operator overloading and common questions
What are some real examples of C++ operator overloading used in production code?
Real‑world examples include:
- Arithmetic on geometry types (
Vec2,Vec3, matrices) in graphics and game engines. - Time and date types that overload
+,-, and comparison operators (similar tostd::chrono). - Money and units libraries that overload arithmetic and comparison to reduce bugs.
- Smart pointers that overload
operator*,operator->, andoperator bool. - Containers that overload
operator[]and iterators that overloadoperator++andoperator*.
These are exactly the kinds of examples of C++ operator overloading: custom operators examples you’ll see in modern codebases.
Can you give an example of a bad use of operator overloading?
A classic anti‑pattern is overloading operator+ to do something unrelated to addition, like writing to a log file:
Logger log;
log + "User logged in"; // This is confusing and non-obvious.
There’s no natural mapping between + and “log this message,” so the code is clever at the expense of readability. A named function like log.info("User logged in"); is far clearer.
How do I choose between a member and a non‑member operator?
As a rule of thumb:
- Use a member operator when the left‑hand operand must be of your type and you don’t need implicit conversions on the left.
- Use a non‑member (often a
friend) when you want symmetric behavior or implicit conversions on both sides, likescalar * vectorandvector * scalar.
Many of the best examples of C++ operator overloading: custom operators examples in the standard library follow this pattern.
Are there performance costs to operator overloading?
The operators themselves are just functions. If you write them efficiently, they’re as fast as calling a named function. The main performance pitfalls come from hidden allocations, unnecessary copies, or surprising side effects. In modern C++, return‑value optimization and move semantics make idiomatic operator overloading very cheap.
Where can I learn more about idiomatic operator overloading in modern C++?
For deeper reading beyond quick examples of C++ operator overloading: custom operators examples, these resources are worth bookmarking:
- The C++ Core Guidelines (free, maintained by experts): https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines
- CPP Reference’s operator overloading overview: https://en.cppreference.com/w/cpp/language/operators
- MIT’s open C++ course material, which often includes operator overloading examples: https://ocw.mit.edu/
Put simply: operator overloading is a sharp tool. Used thoughtfully, it lets your types behave like first‑class citizens in the language. Misused, it turns your code into a guessing game. The examples of C++ operator overloading: custom operators examples above should give you a solid, modern baseline for doing it right.
Related Topics
Practical examples of polymorphism in C++: function overloading
Best examples of dynamic memory allocation in C++: new and delete
Practical examples of C# variable declaration and initialization examples
Modern examples of C++ operator overloading: custom operators examples that actually matter
Best examples of C# conditional statements: examples & explanations
Real‑world examples of C# exception handling: 3 practical patterns every developer should know
Explore More C++ Code Snippets
Discover more examples and insights in this category.
View All C++ Code Snippets