Defining operator+ (or ==, <<, and most other operators) as a member or free function lets instances of a custom class respond to that operator the same way built-in types do. This is precisely how std::string implements + for concatenation and std::vector implements [] for indexing — there's no special compiler-only privilege involved, any class can do the same. Operators like << (for std::cout output) are conventionally implemented as free functions rather than members, since the left-hand operand (the stream) isn't the class being extended.
Overloading + as a member function
a + b becomes a genuinely readable expression instead of a manually-named method call like a.add(b) — the operator syntax reads naturally once it's defined.
class Vector2D {
public:
double x, y;
Vector2D operator+(const Vector2D& other) const {
return Vector2D{x + other.x, y + other.y};
}
};
Vector2D a{1, 2}, b{3, 4};
Vector2D c = a + b; // calls operator+ — c is {4, 6}Overloading == and <<: comparison and output
operator<< is conventionally a free (non-member) function, since the left operand is std::cout, not the Vector2D itself — a member function couldn't be called that way.
class Vector2D {
public:
double x, y;
bool operator==(const Vector2D& other) const {
return x == other.x && y == other.y;
}
};
// free function — the left operand is ostream, not Vector2D
std::ostream& operator<<(std::ostream& os, const Vector2D& v) {
os << "(" << v.x << ", " << v.y << ")";
return os;
}
Vector2D p{1, 2}, q{1, 2};
std::cout << (p == q) << "\n"; // 1 (true)
std::cout << p << "\n"; // (1, 2)A real guideline: only overload where the meaning is genuinely obvious
+ for vector addition or string concatenation is intuitive; overloading + to mean something unrelated and surprising (like "merge two unrelated database records") makes code harder to read, not easier — the whole benefit of operator overloading depends on the operator's meaning staying close to its conventional mathematical or intuitive sense.