C++ lets a class define what standard operators (+, ==, <<, [], and more) mean when applied to its own objects, so custom types can be used as naturally as built-in ones.
class Vector2D {
public:
double x, y;
Vector2D(double x, double y) : x(x), y(y) {}
Vector2D operator+(const Vector2D &other) const { // member function overload
return Vector2D(x + other.x, y + other.y);
}
bool operator==(const Vector2D &other) const {
return x == other.x && y == other.y;
}
};
// 'friend' lets a non-member function access private members if needed
std::ostream& operator<<(std::ostream &os, const Vector2D &v) {
os << "(" << v.x << ", " << v.y << ")";
return os;
}
int main() {
Vector2D a(1, 2), b(3, 4);
Vector2D c = a + b; // calls operator+
std::cout << c << "\n"; // calls operator<<, prints "(4, 6)"
return 0;
}
Most operators are overloaded as member functions (operator+), since they naturally act on this; operator<< (stream insertion) is the classic exception — it must be written as a standalone (often friend) function, because the left-hand operand is std::ostream (std::cout), not the class being printed, and you can't add a member function to a type you don't own.
Operator overloading should preserve the operator's normal, expected meaning — overloading + to do something unrelated to addition (say, subtraction) technically compiles but violates every reader's intuition and is considered poor style. It's a powerful tool for making mathematical or container-like types (vectors, matrices, custom string types) feel like they belong in the language, used heavily throughout the standard library itself — std::string's +, std::vector's [], and std::cout's << are all just operator overloads.