oops · high
C++ Operator Overloading — Member vs Non-member, Spaceship, Conversion Operators
Operator overloading. Rule: arithmetic (+, -, *) should be non-member free functions for symmetry (scalar*vec and vec*scalar). Compound assignment (+=, -=) should be members that return *this for chaining. Stream operators (<<, >>) must be non-member since ostream is on the left. Prefix ++: increment and return reference (no copy). Postfix ++: save copy, increment (call prefix), return old copy; dummy int parameter distinguishes them. Subscript []: two overloads — non-const returns reference (enables v[0]=5); const returns value. operator(): functor/callable object pattern; captures state unlike function pointers. Comparison: C++20 spaceship auto operator<=>(const T&) const = default — generates all 6 comparisons; also add bool operator==(const T&) const = default explicitly. Conversion operators: always mark explicit to prevent silent implicit conversion bugs. Never overload &&, ||, comma — they lose short-circuit / sequencing semantics. Canonical: implement + in terms of +=; Vec2 operator+(Vec2 a, Vec2 b) { return a += b; }.
Arithmetic: non-member (symmetric); compound assignment: member returns *this; prefix++: returns ref; postfix++: returns copy (dummy int param); operator[]: two overloads (const/non-const); C++20 <=>: generates all 6 comparisons; explicit operator bool; NEVER overload &&, ||, comma.
The code
// Operator Overloading — idiomatic C++
struct Vec2 { double x, y;
// Arithmetic: prefer non-member (symmetric: int+Vec2 and Vec2+int) Vec2 operator+(Vec2 rhs) const { return {x + rhs.x, y + rhs.y}; } Vec2 operator-(Vec2 rhs) const { return {x - rhs.x, y - rhs.y}; } Vec2 operator*(double s) const { return {x * s, y * s}; }
// Compound assignment: member (modifies *this) Vec2& operator+=(Vec2 rhs) { x += rhs.x; y += rhs.y; return *this; } Vec2& operator-=(Vec2 rhs) { x -= rhs.x; y -= rhs.y; return *this; } Vec2& operator*=(double s) { x *= s; y *= s; return *this; }
// Comparison: prefer C++20 <=> (spaceship) or free function pairs bool operator==(Vec2 o) const { return x == o.x && y == o.y; } // In C++20: auto operator<=>(Vec2) const = default; (all 6 in one)
// Subscript: return ref to allow assignment (v[0] = 5) double& operator[](int i) { return i == 0 ? x : y; } double operator[](int i) const { return i == 0 ? x : y; } // const overload
// Unary minus: new value (doesn't change *this) Vec2 operator-() const { return {-x, -y}; }
// Stream insertion: free function (needs access to private? friend) friend std::ostream& operator<<(std::ostream& os, Vec2 v) { return os << '(' << v.x << ',' << v.y << ')'; }};
// Non-member: enables: 2.0 * Vec2{1,2} (scalar on left)Vec2 operator*(double s, Vec2 v) { return v * s;}
// Prefix ++: return ref (efficient)Vec2& operator++(Vec2& v) { v.x += 1; return v;}
// Postfix ++: return copy (takes dummy int)Vec2 operator++(Vec2& v, int) { Vec2 tmp = v; ++v; return tmp;}
// operator(): functor / callable objectstruct Multiplier { double factor; double operator()(double x) const { return x * factor; }};// Multiplier times2{2.0}; times2(5.0) → 10.0
// Conversion operator: explicit to prevent surprise implicit conversionsstruct Nullable { bool valid_; explicit operator bool() const { return valid_; } // if(n) {...} works; int x = n; COMPILE ERROR (explicit blocks it)};
// RULE: don't overload &&, ||, comma — they lose short-circuit semantics!// RULE: += must match + (a += b ≡ a = a + b)// RULE: overload <=> in C++20 instead of 6 separate comparison operatorsWhat this lesson walks through
- 01Member vs non-member — the symmetric argument rule
- 02Pre-increment vs post-increment — the dummy int idiom
- 03Subscript operator — const and non-const overloads
- 04Comparison operators — C++20 spaceship operator <=>
- 05Conversion operators and explicit keyword
- 06Operators NOT to overload + canonical guidelines
Operator overloading redefines what operators mean for user-defined types. Key rule: arithmetic operators (+, -, *) should be non-member free functions so they're symmetric — 2.0*v and v*2.0 both work without writing two different member operators. Compound assignment operators (+=, -=) should be members since they modify *this. Stream operators (<<, >>) must be non-member since the left-hand side is ostream, not your class.
See it animated — step by step, at your own pace
Unlock the full interactive walkthrough of C++ Operator Overloading — Member vs Non-member, Spaceship, Conversion Operators and 100+ animated C++ interview lessons.